mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
bootloader: Add fault injection resistance to Secure Boot bootloader verification
Goal is that multiple faults would be required to bypass a boot-time signature check. - Also strengthens some address range checks for safe app memory addresses - Change pre-enable logic to also check the bootloader signature before enabling SBV2 on ESP32 Add some additional checks for invalid sections: - Sections only partially in DRAM or IRAM are invalid - If a section is in D/IRAM, allow the possibility only some is in D/IRAM - Only pass sections that are entirely in the same type of RTC memory region
This commit is contained in:
parent
0dacff4df4
commit
d40c69375c
@ -1,3 +1,3 @@
|
|||||||
# only compile the "micro-ecc/uECC.c" source file
|
# only compile the "uECC_verify_antifault.c" file which includes the "micro-ecc/uECC.c" source file
|
||||||
idf_component_register(SRCS "micro-ecc/uECC.c"
|
idf_component_register(SRCS "uECC_verify_antifault.c"
|
||||||
INCLUDE_DIRS micro-ecc)
|
INCLUDE_DIRS . micro-ecc)
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
# only compile the micro-ecc/uECC.c source file
|
# only compile the "uECC_verify_antifault.c" file which includes the "micro-ecc/uECC.c" source file
|
||||||
# (SRCDIRS is needed so build system can find the source file)
|
COMPONENT_SRCDIRS := .
|
||||||
COMPONENT_SRCDIRS := micro-ecc
|
|
||||||
COMPONENT_OBJS := micro-ecc/uECC.o
|
|
||||||
|
|
||||||
COMPONENT_ADD_INCLUDEDIRS := micro-ecc
|
COMPONENT_ADD_INCLUDEDIRS := . micro-ecc
|
||||||
|
|
||||||
COMPONENT_SUBMODULES := micro-ecc
|
COMPONENT_SUBMODULES := micro-ecc
|
||||||
|
@ -0,0 +1,141 @@
|
|||||||
|
/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license.
|
||||||
|
|
||||||
|
Modifications Copyright 2020, Espressif Systems (Shanghai) PTE LTD. Licensed under the BSD
|
||||||
|
2-clause license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* uECC_verify() calls a number of static functions form here and
|
||||||
|
uses other definitions, so we just build that whole source file here and then append
|
||||||
|
our modified version uECC_verify_antifault(). */
|
||||||
|
#include "micro-ecc/uECC.c"
|
||||||
|
|
||||||
|
/* Version of uECC_verify() which also copies message_hash into verified_hash,
|
||||||
|
but only if the signature is valid. Does this in an FI resistant way.
|
||||||
|
*/
|
||||||
|
int uECC_verify_antifault(const uint8_t *public_key,
|
||||||
|
const uint8_t *message_hash,
|
||||||
|
unsigned hash_size,
|
||||||
|
const uint8_t *signature,
|
||||||
|
uECC_Curve curve,
|
||||||
|
uint8_t *verified_hash) {
|
||||||
|
uECC_word_t u1[uECC_MAX_WORDS], u2[uECC_MAX_WORDS];
|
||||||
|
uECC_word_t z[uECC_MAX_WORDS];
|
||||||
|
uECC_word_t sum[uECC_MAX_WORDS * 2];
|
||||||
|
uECC_word_t rx[uECC_MAX_WORDS];
|
||||||
|
uECC_word_t ry[uECC_MAX_WORDS];
|
||||||
|
uECC_word_t tx[uECC_MAX_WORDS];
|
||||||
|
uECC_word_t ty[uECC_MAX_WORDS];
|
||||||
|
uECC_word_t tz[uECC_MAX_WORDS];
|
||||||
|
const uECC_word_t *points[4];
|
||||||
|
const uECC_word_t *point;
|
||||||
|
bitcount_t num_bits;
|
||||||
|
bitcount_t i;
|
||||||
|
#if uECC_VLI_NATIVE_LITTLE_ENDIAN
|
||||||
|
uECC_word_t *_public = (uECC_word_t *)public_key;
|
||||||
|
#else
|
||||||
|
uECC_word_t _public[uECC_MAX_WORDS * 2];
|
||||||
|
#endif
|
||||||
|
uECC_word_t r[uECC_MAX_WORDS], s[uECC_MAX_WORDS];
|
||||||
|
wordcount_t num_words = curve->num_words;
|
||||||
|
wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits);
|
||||||
|
|
||||||
|
rx[num_n_words - 1] = 0;
|
||||||
|
r[num_n_words - 1] = 0;
|
||||||
|
s[num_n_words - 1] = 0;
|
||||||
|
|
||||||
|
#if uECC_VLI_NATIVE_LITTLE_ENDIAN
|
||||||
|
bcopy((uint8_t *) r, signature, curve->num_bytes);
|
||||||
|
bcopy((uint8_t *) s, signature + curve->num_bytes, curve->num_bytes);
|
||||||
|
#else
|
||||||
|
uECC_vli_bytesToNative(_public, public_key, curve->num_bytes);
|
||||||
|
uECC_vli_bytesToNative(
|
||||||
|
_public + num_words, public_key + curve->num_bytes, curve->num_bytes);
|
||||||
|
uECC_vli_bytesToNative(r, signature, curve->num_bytes);
|
||||||
|
uECC_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* r, s must not be 0. */
|
||||||
|
if (uECC_vli_isZero(r, num_words) || uECC_vli_isZero(s, num_words)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* r, s must be < n. */
|
||||||
|
if (uECC_vli_cmp(curve->n, r, num_n_words) != 1 ||
|
||||||
|
uECC_vli_cmp(curve->n, s, num_n_words) != 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate u1 and u2. */
|
||||||
|
uECC_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */
|
||||||
|
u1[num_n_words - 1] = 0;
|
||||||
|
bits2int(u1, message_hash, hash_size, curve);
|
||||||
|
uECC_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */
|
||||||
|
uECC_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */
|
||||||
|
|
||||||
|
/* Calculate sum = G + Q. */
|
||||||
|
uECC_vli_set(sum, _public, num_words);
|
||||||
|
uECC_vli_set(sum + num_words, _public + num_words, num_words);
|
||||||
|
uECC_vli_set(tx, curve->G, num_words);
|
||||||
|
uECC_vli_set(ty, curve->G + num_words, num_words);
|
||||||
|
uECC_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */
|
||||||
|
XYcZ_add(tx, ty, sum, sum + num_words, curve);
|
||||||
|
uECC_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */
|
||||||
|
apply_z(sum, sum + num_words, z, curve);
|
||||||
|
|
||||||
|
/* Use Shamir's trick to calculate u1*G + u2*Q */
|
||||||
|
points[0] = 0;
|
||||||
|
points[1] = curve->G;
|
||||||
|
points[2] = _public;
|
||||||
|
points[3] = sum;
|
||||||
|
num_bits = smax(uECC_vli_numBits(u1, num_n_words),
|
||||||
|
uECC_vli_numBits(u2, num_n_words));
|
||||||
|
|
||||||
|
point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) |
|
||||||
|
((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)];
|
||||||
|
uECC_vli_set(rx, point, num_words);
|
||||||
|
uECC_vli_set(ry, point + num_words, num_words);
|
||||||
|
uECC_vli_clear(z, num_words);
|
||||||
|
z[0] = 1;
|
||||||
|
|
||||||
|
for (i = num_bits - 2; i >= 0; --i) {
|
||||||
|
uECC_word_t index;
|
||||||
|
curve->double_jacobian(rx, ry, z, curve);
|
||||||
|
|
||||||
|
index = (!!uECC_vli_testBit(u1, i)) | ((!!uECC_vli_testBit(u2, i)) << 1);
|
||||||
|
point = points[index];
|
||||||
|
if (point) {
|
||||||
|
uECC_vli_set(tx, point, num_words);
|
||||||
|
uECC_vli_set(ty, point + num_words, num_words);
|
||||||
|
apply_z(tx, ty, z, curve);
|
||||||
|
uECC_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */
|
||||||
|
XYcZ_add(tx, ty, rx, ry, curve);
|
||||||
|
uECC_vli_modMult_fast(z, z, tz, curve);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uECC_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */
|
||||||
|
apply_z(rx, ry, z, curve);
|
||||||
|
|
||||||
|
/* v = x1 (mod n) */
|
||||||
|
if (uECC_vli_cmp(curve->n, rx, num_n_words) != 1) {
|
||||||
|
uECC_vli_sub(rx, rx, curve->n, num_n_words);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Anti-FI addition. Copy message_hash into verified_hash, but do it in a
|
||||||
|
way that it will only happen if v == r (ie, rx == r)
|
||||||
|
*/
|
||||||
|
const uECC_word_t *mhash_words = (const uECC_word_t *)message_hash;
|
||||||
|
uECC_word_t *vhash_words = (uECC_word_t *)verified_hash;
|
||||||
|
unsigned hash_words = hash_size / sizeof(uECC_word_t);
|
||||||
|
for (int w = 0; w < hash_words; w++) {
|
||||||
|
/* note: using curve->num_words here to encourage compiler to re-read this variable */
|
||||||
|
vhash_words[w] = mhash_words[w] ^ rx[w % curve->num_words] ^ r[w % curve->num_words];
|
||||||
|
}
|
||||||
|
/* Curve may be longer than hash, in which case keep reading the rest of the bytes */
|
||||||
|
for (int w = hash_words; w < curve->num_words; w++) {
|
||||||
|
vhash_words[w % hash_words] |= rx[w] | r[w];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accept only if v == r. */
|
||||||
|
return (int)(uECC_vli_equal(rx, r, num_words));
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license.
|
||||||
|
|
||||||
|
Modifications Copyright 2020, Espressif Systems (Shanghai) PTE LTD. Licensed under the BSD
|
||||||
|
2-clause license.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "uECC.h"
|
||||||
|
|
||||||
|
/* Version uECC_verify() that also copies message_hash to verified_hash
|
||||||
|
if the signature is valid, and does it in a way that is harder to attack
|
||||||
|
with fault injection.
|
||||||
|
*/
|
||||||
|
int uECC_verify_antifault(const uint8_t *public_key,
|
||||||
|
const uint8_t *message_hash,
|
||||||
|
unsigned hash_size,
|
||||||
|
const uint8_t *signature,
|
||||||
|
uECC_Curve curve,
|
||||||
|
uint8_t *verified_hash);
|
@ -74,6 +74,7 @@ SECTIONS
|
|||||||
.dram0.bss (NOLOAD) :
|
.dram0.bss (NOLOAD) :
|
||||||
{
|
{
|
||||||
. = ALIGN (8);
|
. = ALIGN (8);
|
||||||
|
_dram_start = ABSOLUTE(.);
|
||||||
_bss_start = ABSOLUTE(.);
|
_bss_start = ABSOLUTE(.);
|
||||||
*(.dynsbss)
|
*(.dynsbss)
|
||||||
*(.sbss)
|
*(.sbss)
|
||||||
@ -150,6 +151,7 @@ SECTIONS
|
|||||||
*(.gnu.linkonce.lit4.*)
|
*(.gnu.linkonce.lit4.*)
|
||||||
_lit4_end = ABSOLUTE(.);
|
_lit4_end = ABSOLUTE(.);
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
|
_dram_end = ABSOLUTE(.);
|
||||||
} >dram_seg
|
} >dram_seg
|
||||||
|
|
||||||
.iram.text :
|
.iram.text :
|
||||||
|
@ -59,6 +59,7 @@ SECTIONS
|
|||||||
.dram0.bss (NOLOAD) :
|
.dram0.bss (NOLOAD) :
|
||||||
{
|
{
|
||||||
. = ALIGN (8);
|
. = ALIGN (8);
|
||||||
|
_dram_start = ABSOLUTE(.);
|
||||||
_bss_start = ABSOLUTE(.);
|
_bss_start = ABSOLUTE(.);
|
||||||
*(.dynsbss)
|
*(.dynsbss)
|
||||||
*(.sbss)
|
*(.sbss)
|
||||||
@ -135,7 +136,7 @@ SECTIONS
|
|||||||
*(.gnu.linkonce.lit4.*)
|
*(.gnu.linkonce.lit4.*)
|
||||||
_lit4_end = ABSOLUTE(.);
|
_lit4_end = ABSOLUTE(.);
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
_heap_start = ABSOLUTE(.);
|
_dram_end = ABSOLUTE(.);
|
||||||
} >dram_seg
|
} >dram_seg
|
||||||
|
|
||||||
.iram.text :
|
.iram.text :
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
#include "esp32/rom/secure_boot.h"
|
#include "esp32/rom/secure_boot.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef struct ets_secure_boot_signature ets_secure_boot_signature_t;
|
||||||
|
|
||||||
#ifdef CONFIG_SECURE_BOOT_V1_ENABLED
|
#ifdef CONFIG_SECURE_BOOT_V1_ENABLED
|
||||||
#if !defined(CONFIG_SECURE_SIGNED_ON_BOOT) || !defined(CONFIG_SECURE_SIGNED_ON_UPDATE) || !defined(CONFIG_SECURE_SIGNED_APPS)
|
#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"
|
#error "internal sdkconfig error, secure boot should always enable all signature options"
|
||||||
@ -149,6 +151,9 @@ esp_err_t esp_secure_boot_v2_permanently_enable(const esp_image_metadata_t *imag
|
|||||||
*
|
*
|
||||||
* If flash encryption is enabled, the image will be transparently decrypted while being verified.
|
* If flash encryption is enabled, the image will be transparently decrypted while being verified.
|
||||||
*
|
*
|
||||||
|
* @note This function doesn't have any fault injection resistance so should not be called
|
||||||
|
* during a secure boot itself (but can be called when verifying an update, etc.)
|
||||||
|
*
|
||||||
* @return ESP_OK if signature is valid, ESP_ERR_INVALID_STATE if
|
* @return ESP_OK if signature is valid, ESP_ERR_INVALID_STATE if
|
||||||
* signature fails, ESP_FAIL for other failures (ie can't read flash).
|
* signature fails, ESP_FAIL for other failures (ie can't read flash).
|
||||||
*/
|
*/
|
||||||
@ -160,22 +165,43 @@ typedef struct {
|
|||||||
uint8_t signature[64];
|
uint8_t signature[64];
|
||||||
} esp_secure_boot_sig_block_t;
|
} esp_secure_boot_sig_block_t;
|
||||||
|
|
||||||
/** @brief Verify the secure boot signature block.
|
/** @brief Verify the ECDSA secure boot signature block for Secure Boot V1.
|
||||||
*
|
*
|
||||||
* For ECDSA Scheme (Secure Boot V1) - Deterministic ECDSA w/ SHA256 based on the SHA256 hash of the image.
|
* Calculates Deterministic ECDSA w/ SHA256 based on the SHA256 hash of the image. ECDSA signature
|
||||||
* For RSA Scheme (Secure Boot V2) - RSA-PSS Verification of the SHA-256 image based on the public key
|
* verification must be enabled in project configuration to use this function.
|
||||||
* in the signature block.
|
|
||||||
*
|
*
|
||||||
* Similar to esp_secure_boot_verify_signature(), but can be used when the digest is precalculated.
|
* Similar to esp_secure_boot_verify_signature(), but can be used when the digest is precalculated.
|
||||||
* @param sig_block Pointer to RSA or ECDSA signature block data
|
* @param sig_block Pointer to ECDSA signature block data
|
||||||
* @param image_digest Pointer to 32 byte buffer holding SHA-256 hash.
|
* @param image_digest Pointer to 32 byte buffer holding SHA-256 hash.
|
||||||
|
* @param verified_digest Pointer to 32 byte buffer that will receive verified digest if verification completes. (Used during bootloader implementation only, result is invalid otherwise.)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
esp_err_t esp_secure_boot_verify_ecdsa_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest);
|
||||||
esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest);
|
|
||||||
#elif CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
|
||||||
esp_err_t esp_secure_boot_verify_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest);
|
/** @brief Verify the RSA secure boot signature block for Secure Boot V2.
|
||||||
#endif
|
*
|
||||||
|
* Performs RSA-PSS Verification of the SHA-256 image based on the public key
|
||||||
|
* in the signature block, compared against the public key digest stored in efuse.
|
||||||
|
*
|
||||||
|
* Similar to esp_secure_boot_verify_signature(), but can be used when the digest is precalculated.
|
||||||
|
* @param sig_block Pointer to RSA signature block data
|
||||||
|
* @param image_digest Pointer to 32 byte buffer holding SHA-256 hash.
|
||||||
|
* @param verified_digest Pointer to 32 byte buffer that will receive verified digest if verification completes. (Used during bootloader implementation only, result is invalid otherwise.)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
/** @brief Legacy ECDSA verification function
|
||||||
|
*
|
||||||
|
* @note Deprecated, call either esp_secure_boot_verify_ecdsa_signature_block() or esp_secure_boot_verify_rsa_signature_block() instead.
|
||||||
|
*
|
||||||
|
* @param sig_block Pointer to ECDSA signature block data
|
||||||
|
* @param image_digest Pointer to 32 byte buffer holding SHA-256 hash.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest)
|
||||||
|
__attribute__((deprecated("use esp_secure_boot_verify_ecdsa_signature_block instead")));
|
||||||
|
|
||||||
|
|
||||||
#define FLASH_OFFS_SECURE_BOOT_IV_DIGEST 0
|
#define FLASH_OFFS_SECURE_BOOT_IV_DIGEST 0
|
||||||
|
|
||||||
|
@ -366,4 +366,4 @@ esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length)
|
|||||||
flash_failed:
|
flash_failed:
|
||||||
ESP_LOGE(TAG, "flash operation failed: 0x%x", err);
|
ESP_LOGE(TAG, "flash operation failed: 0x%x", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -263,7 +263,7 @@ static esp_err_t secure_boot_v2_digest_generate(uint32_t flash_offset, uint32_t
|
|||||||
/* Validating Signature block */
|
/* Validating Signature block */
|
||||||
ret = validate_signature_block(sig_block, image_digest);
|
ret = validate_signature_block(sig_block, image_digest);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "signature block validation failed %d", ret);
|
ESP_LOGE(TAG, "signature block (address 0x%x) validation failed %d", sig_block_addr, ret);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,7 +329,7 @@ esp_err_t esp_secure_boot_v2_permanently_enable(const esp_image_metadata_t *imag
|
|||||||
&& REG_READ(EFUSE_BLK2_RDATA6_REG) == 0
|
&& REG_READ(EFUSE_BLK2_RDATA6_REG) == 0
|
||||||
&& REG_READ(EFUSE_BLK2_RDATA7_REG) == 0) {
|
&& REG_READ(EFUSE_BLK2_RDATA7_REG) == 0) {
|
||||||
/* Verifies the signature block appended to the image matches with the signature block of the app to be loaded */
|
/* Verifies the signature block appended to the image matches with the signature block of the app to be loaded */
|
||||||
ret = secure_boot_v2_digest_generate(bootloader_data.start_addr, bootloader_data.image_len, boot_pub_key_digest);
|
ret = secure_boot_v2_digest_generate(bootloader_data.start_addr, bootloader_data.image_len - SIG_BLOCK_PADDING, boot_pub_key_digest);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Public key digest generation failed");
|
ESP_LOGE(TAG, "Public key digest generation failed");
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -20,8 +20,9 @@
|
|||||||
#include "esp_image_format.h"
|
#include "esp_image_format.h"
|
||||||
#include "esp_secure_boot.h"
|
#include "esp_secure_boot.h"
|
||||||
#include "esp_spi_flash.h"
|
#include "esp_spi_flash.h"
|
||||||
|
#include "esp_fault.h"
|
||||||
#include "esp32/rom/sha.h"
|
#include "esp32/rom/sha.h"
|
||||||
#include "uECC.h"
|
#include "uECC_verify_antifault.h"
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -38,10 +39,11 @@ extern const uint8_t signature_verification_key_end[] asm("_binary_signature_ver
|
|||||||
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
||||||
{
|
{
|
||||||
uint8_t digest[DIGEST_LEN];
|
uint8_t digest[DIGEST_LEN];
|
||||||
|
uint8_t verified_digest[DIGEST_LEN] = { 0 }; /* ignored in this function */
|
||||||
const esp_secure_boot_sig_block_t *sigblock;
|
const esp_secure_boot_sig_block_t *sigblock;
|
||||||
|
|
||||||
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
|
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
|
||||||
|
|
||||||
esp_err_t err = bootloader_sha256_flash_contents(src_addr, length, digest);
|
esp_err_t err = bootloader_sha256_flash_contents(src_addr, length, digest);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
@ -54,7 +56,7 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
|||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
// Verify the signature
|
// Verify the signature
|
||||||
err = esp_secure_boot_verify_signature_block(sigblock, digest);
|
err = esp_secure_boot_verify_ecdsa_signature_block(sigblock, digest, verified_digest);
|
||||||
// Unmap
|
// Unmap
|
||||||
bootloader_munmap(sigblock);
|
bootloader_munmap(sigblock);
|
||||||
|
|
||||||
@ -62,6 +64,12 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
|||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest)
|
esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest)
|
||||||
|
{
|
||||||
|
uint8_t verified_digest[DIGEST_LEN] = { 0 };
|
||||||
|
return esp_secure_boot_verify_ecdsa_signature_block(sig_block, image_digest, verified_digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_secure_boot_verify_ecdsa_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest)
|
||||||
{
|
{
|
||||||
ptrdiff_t keylen;
|
ptrdiff_t keylen;
|
||||||
|
|
||||||
@ -79,12 +87,14 @@ esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block
|
|||||||
ESP_LOGD(TAG, "Verifying secure boot signature");
|
ESP_LOGD(TAG, "Verifying secure boot signature");
|
||||||
|
|
||||||
bool is_valid;
|
bool is_valid;
|
||||||
is_valid = uECC_verify(signature_verification_key_start,
|
is_valid = uECC_verify_antifault(signature_verification_key_start,
|
||||||
image_digest,
|
image_digest,
|
||||||
DIGEST_LEN,
|
DIGEST_LEN,
|
||||||
sig_block->signature,
|
sig_block->signature,
|
||||||
uECC_secp256r1());
|
uECC_secp256r1(),
|
||||||
|
verified_digest);
|
||||||
ESP_LOGD(TAG, "Verification result %d", is_valid);
|
ESP_LOGD(TAG, "Verification result %d", is_valid);
|
||||||
|
|
||||||
return is_valid ? ESP_OK : ESP_ERR_IMAGE_INVALID;
|
return is_valid ? ESP_OK : ESP_ERR_IMAGE_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +104,7 @@ esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block
|
|||||||
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
||||||
{
|
{
|
||||||
uint8_t digest[DIGEST_LEN] = {0};
|
uint8_t digest[DIGEST_LEN] = {0};
|
||||||
|
uint8_t verified_digest[DIGEST_LEN] = {0}; // ignored in this function
|
||||||
const uint8_t *data;
|
const uint8_t *data;
|
||||||
|
|
||||||
/* Padding to round off the input to the nearest 4k boundary */
|
/* Padding to round off the input to the nearest 4k boundary */
|
||||||
@ -115,7 +126,7 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ets_secure_boot_signature_t *sig_block = (const ets_secure_boot_signature_t *)(data + padded_length);
|
const ets_secure_boot_signature_t *sig_block = (const ets_secure_boot_signature_t *)(data + padded_length);
|
||||||
err = esp_secure_boot_verify_signature_block(sig_block, digest);
|
err = esp_secure_boot_verify_rsa_signature_block(sig_block, digest, verified_digest);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Secure Boot V2 verification failed.");
|
ESP_LOGE(TAG, "Secure Boot V2 verification failed.");
|
||||||
}
|
}
|
||||||
@ -124,16 +135,16 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t esp_secure_boot_verify_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest)
|
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)
|
||||||
{
|
{
|
||||||
uint8_t efuse_trusted_digest[DIGEST_LEN] = {0}, sig_block_trusted_digest[DIGEST_LEN] = {0}, verified_digest[DIGEST_LEN] = {0};
|
uint8_t efuse_trusted_digest[DIGEST_LEN] = {0}, sig_block_trusted_digest[DIGEST_LEN] = {0};
|
||||||
|
|
||||||
secure_boot_v2_status_t r;
|
secure_boot_v2_status_t r;
|
||||||
memcpy(efuse_trusted_digest, (uint8_t *)EFUSE_BLK2_RDATA0_REG, DIGEST_LEN); /* EFUSE_BLK2_RDATA0_REG - Stores the Secure Boot Public Key Digest */
|
memcpy(efuse_trusted_digest, (uint8_t *)EFUSE_BLK2_RDATA0_REG, DIGEST_LEN); /* EFUSE_BLK2_RDATA0_REG - Stores the Secure Boot Public Key Digest */
|
||||||
|
|
||||||
if (!ets_use_secure_boot_v2()) {
|
if (!ets_use_secure_boot_v2()) {
|
||||||
ESP_LOGI(TAG, "Secure Boot EFuse bit(ABS_DONE_1) not yet programmed.");
|
ESP_LOGI(TAG, "Secure Boot EFuse bit(ABS_DONE_1) not yet programmed.");
|
||||||
|
|
||||||
/* Generating the SHA of the public key components in the signature block */
|
/* Generating the SHA of the public key components in the signature block */
|
||||||
bootloader_sha256_handle_t sig_block_sha;
|
bootloader_sha256_handle_t sig_block_sha;
|
||||||
sig_block_sha = bootloader_sha256_start();
|
sig_block_sha = bootloader_sha256_start();
|
||||||
@ -155,4 +166,4 @@ esp_err_t esp_secure_boot_verify_signature_block(const ets_secure_boot_signature
|
|||||||
|
|
||||||
return (r == SBV2_SUCCESS) ? ESP_OK : ESP_ERR_IMAGE_INVALID;
|
return (r == SBV2_SUCCESS) ? ESP_OK : ESP_ERR_IMAGE_INVALID;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -27,6 +27,7 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
|||||||
{
|
{
|
||||||
ets_secure_boot_key_digests_t trusted_keys = { 0 };
|
ets_secure_boot_key_digests_t trusted_keys = { 0 };
|
||||||
uint8_t digest[DIGEST_LEN];
|
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;
|
const uint8_t *data;
|
||||||
|
|
||||||
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
|
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
|
||||||
@ -57,14 +58,14 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
|||||||
if (r == ETS_OK) {
|
if (r == ETS_OK) {
|
||||||
const ets_secure_boot_signature_t *sig = (const ets_secure_boot_signature_t *)(data + length);
|
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
|
// TODO: calling this function in IDF app context is unsafe
|
||||||
r = ets_secure_boot_verify_signature(sig, digest, &trusted_keys);
|
r = ets_secure_boot_verify_signature(sig, digest, &trusted_keys, verified_digest);
|
||||||
}
|
}
|
||||||
bootloader_munmap(data);
|
bootloader_munmap(data);
|
||||||
|
|
||||||
return (r == ETS_OK) ? ESP_OK : ESP_FAIL;
|
return (r == ETS_OK) ? ESP_OK : ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t esp_secure_boot_verify_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest)
|
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_keys;
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ esp_err_t esp_secure_boot_verify_signature_block(const ets_secure_boot_signature
|
|||||||
} else {
|
} else {
|
||||||
ESP_LOGD(TAG, "Verifying with RSA-PSS...");
|
ESP_LOGD(TAG, "Verifying with RSA-PSS...");
|
||||||
// TODO: calling this function in IDF app context is unsafe
|
// TODO: calling this function in IDF app context is unsafe
|
||||||
r = ets_secure_boot_verify_signature(sig_block, image_digest, &trusted_keys);
|
r = ets_secure_boot_verify_signature(sig_block, image_digest, &trusted_keys, verified_digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (r == 0) ? ESP_OK : ESP_ERR_IMAGE_INVALID;
|
return (r == 0) ? ESP_OK : ESP_ERR_IMAGE_INVALID;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <soc/cpu.h>
|
#include <soc/cpu.h>
|
||||||
#include <bootloader_utility.h>
|
#include <bootloader_utility.h>
|
||||||
#include <esp_secure_boot.h>
|
#include <esp_secure_boot.h>
|
||||||
|
#include <esp_fault.h>
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
#include <esp_spi_flash.h>
|
#include <esp_spi_flash.h>
|
||||||
#include <bootloader_flash.h>
|
#include <bootloader_flash.h>
|
||||||
@ -23,6 +24,7 @@
|
|||||||
#include <bootloader_sha.h>
|
#include <bootloader_sha.h>
|
||||||
#include "bootloader_util.h"
|
#include "bootloader_util.h"
|
||||||
#include "bootloader_common.h"
|
#include "bootloader_common.h"
|
||||||
|
#include "soc/soc_memory_layout.h"
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
#include "esp32/rom/rtc.h"
|
#include "esp32/rom/rtc.h"
|
||||||
#include "esp32/rom/secure_boot.h"
|
#include "esp32/rom/secure_boot.h"
|
||||||
@ -37,11 +39,11 @@
|
|||||||
*/
|
*/
|
||||||
#ifdef BOOTLOADER_BUILD
|
#ifdef BOOTLOADER_BUILD
|
||||||
#ifdef CONFIG_SECURE_SIGNED_ON_BOOT
|
#ifdef CONFIG_SECURE_SIGNED_ON_BOOT
|
||||||
#define SECURE_BOOT_CHECK_SIGNATURE
|
#define SECURE_BOOT_CHECK_SIGNATURE 1
|
||||||
#endif
|
#endif
|
||||||
#else /* !BOOTLOADER_BUILD */
|
#else /* !BOOTLOADER_BUILD */
|
||||||
#ifdef CONFIG_SECURE_SIGNED_ON_UPDATE
|
#ifdef CONFIG_SECURE_SIGNED_ON_UPDATE
|
||||||
#define SECURE_BOOT_CHECK_SIGNATURE
|
#define SECURE_BOOT_CHECK_SIGNATURE 1
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -61,9 +63,6 @@ static const char *TAG = "esp_image";
|
|||||||
*/
|
*/
|
||||||
static uint32_t ram_obfs_value[2];
|
static uint32_t ram_obfs_value[2];
|
||||||
|
|
||||||
/* Range of IRAM used by the loader, defined in ld script */
|
|
||||||
extern int _loader_text_start;
|
|
||||||
extern int _loader_text_end;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Return true if load_addr is an address the bootloader should load into */
|
/* Return true if load_addr is an address the bootloader should load into */
|
||||||
@ -94,7 +93,7 @@ static esp_err_t verify_segment_header(int index, const esp_image_segment_header
|
|||||||
|
|
||||||
static esp_err_t verify_checksum(bootloader_sha256_handle_t sha_handle, uint32_t checksum_word, esp_image_metadata_t *data);
|
static esp_err_t verify_checksum(bootloader_sha256_handle_t sha_handle, uint32_t checksum_word, esp_image_metadata_t *data);
|
||||||
|
|
||||||
static esp_err_t __attribute__((unused)) verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data);
|
static esp_err_t __attribute__((unused)) verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data, uint8_t *image_digest, uint8_t *verified_digest);
|
||||||
static esp_err_t __attribute__((unused)) verify_simple_hash(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data);
|
static esp_err_t __attribute__((unused)) verify_simple_hash(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data);
|
||||||
|
|
||||||
static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data)
|
static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data)
|
||||||
@ -112,6 +111,11 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
|
|||||||
uint32_t checksum_word = ESP_ROM_CHECKSUM_INITIAL;
|
uint32_t checksum_word = ESP_ROM_CHECKSUM_INITIAL;
|
||||||
uint32_t *checksum = NULL;
|
uint32_t *checksum = NULL;
|
||||||
bootloader_sha256_handle_t sha_handle = NULL;
|
bootloader_sha256_handle_t sha_handle = NULL;
|
||||||
|
#if SECURE_BOOT_CHECK_SIGNATURE
|
||||||
|
/* used for anti-FI checks */
|
||||||
|
uint8_t image_digest[HASH_LEN] = { [ 0 ... 31] = 0xEE };
|
||||||
|
uint8_t verified_digest[HASH_LEN] = { [ 0 ... 31 ] = 0x01 };
|
||||||
|
#endif
|
||||||
|
|
||||||
if (data == NULL || part == NULL) {
|
if (data == NULL || part == NULL) {
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
@ -169,6 +173,7 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
|
|||||||
for (int i = 0; i < data->image.segment_count; i++) {
|
for (int i = 0; i < data->image.segment_count; i++) {
|
||||||
esp_image_segment_header_t *header = &data->segments[i];
|
esp_image_segment_header_t *header = &data->segments[i];
|
||||||
ESP_LOGV(TAG, "loading segment header %d at offset 0x%x", i, next_addr);
|
ESP_LOGV(TAG, "loading segment header %d at offset 0x%x", i, next_addr);
|
||||||
|
|
||||||
err = process_segment(i, next_addr, header, silent, do_load, sha_handle, checksum);
|
err = process_segment(i, next_addr, header, silent, do_load, sha_handle, checksum);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
goto err;
|
goto err;
|
||||||
@ -194,14 +199,14 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For secure boot on ESP32, we don't calculate SHA or verify signautre on bootloaders.
|
/* For secure boot V1 on ESP32, we don't calculate SHA or verify signature on bootloaders.
|
||||||
For ESP32S2, we do verify signature on bootloader which includes the SHA calculation.
|
For Secure Boot V2, we do verify signature on bootloader which includes the SHA calculation.
|
||||||
|
|
||||||
(For non-secure boot, we don't verify any SHA-256 hash appended to the bootloader because
|
(For non-secure boot, we don't verify any SHA-256 hash appended to the bootloader because
|
||||||
esptool.py may have rewritten the header - rely on esptool.py having verified the bootloader at flashing time, instead.)
|
esptool.py may have rewritten the header - rely on esptool.py having verified the bootloader at flashing time, instead.)
|
||||||
*/
|
*/
|
||||||
bool verify_sha;
|
bool verify_sha;
|
||||||
#if CONFIG_SECURE_BOOT_V2_ENABLED && CONFIG_IDF_TARGET_ESP32S2
|
#if CONFIG_SECURE_BOOT_V2_ENABLED
|
||||||
verify_sha = true;
|
verify_sha = true;
|
||||||
#else // ESP32, or ESP32S2 without secure boot enabled
|
#else // ESP32, or ESP32S2 without secure boot enabled
|
||||||
verify_sha = (data->start_addr != ESP_BOOTLOADER_OFFSET);
|
verify_sha = (data->start_addr != ESP_BOOTLOADER_OFFSET);
|
||||||
@ -214,7 +219,7 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
|
|||||||
|
|
||||||
#ifdef SECURE_BOOT_CHECK_SIGNATURE
|
#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, image_digest, verified_digest);
|
||||||
#else
|
#else
|
||||||
// No secure boot, but SHA-256 can be appended for basic corruption detection
|
// No secure boot, but SHA-256 can be appended for basic corruption detection
|
||||||
if (sha_handle != NULL && !esp_cpu_in_ocd_debug_mode()) {
|
if (sha_handle != NULL && !esp_cpu_in_ocd_debug_mode()) {
|
||||||
@ -247,7 +252,28 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BOOTLOADER_BUILD
|
#ifdef BOOTLOADER_BUILD
|
||||||
if (do_load && ram_obfs_value[0] != 0 && ram_obfs_value[1] != 0) { // Need to deobfuscate RAM
|
|
||||||
|
#ifdef SECURE_BOOT_CHECK_SIGNATURE
|
||||||
|
/* If signature was checked in bootloader build, verified_digest should equal image_digest
|
||||||
|
|
||||||
|
This is to detect any fault injection that caused signature verification to not complete normally.
|
||||||
|
|
||||||
|
Any attack which bypasses this check should be of limited use as the RAM contents are still obfuscated, therefore we do the check
|
||||||
|
immediately before we deobfuscate.
|
||||||
|
|
||||||
|
Note: the conditions for making this check are the same as for setting verify_sha above, but on ESP32 SB V1 we move the test for
|
||||||
|
"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);
|
||||||
|
#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
|
||||||
|
|
||||||
|
#endif // SECURE_BOOT_CHECK_SIGNATURE
|
||||||
|
|
||||||
|
// Deobfuscate RAM
|
||||||
|
if (do_load && ram_obfs_value[0] != 0 && ram_obfs_value[1] != 0) {
|
||||||
for (int i = 0; i < data->image.segment_count; i++) {
|
for (int i = 0; i < data->image.segment_count; i++) {
|
||||||
uint32_t load_addr = data->segments[i].load_addr;
|
uint32_t load_addr = data->segments[i].load_addr;
|
||||||
if (should_load(load_addr)) {
|
if (should_load(load_addr)) {
|
||||||
@ -333,6 +359,127 @@ static esp_err_t verify_image_header(uint32_t src_addr, const esp_image_header_t
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef BOOTLOADER_BUILD
|
||||||
|
/* Check the region load_addr - load_end doesn't overlap any memory used by the bootloader, registers, or other invalid memory
|
||||||
|
*/
|
||||||
|
static bool verify_load_addresses(int segment_index, intptr_t load_addr, intptr_t load_end, bool print_error, bool no_recurse)
|
||||||
|
{
|
||||||
|
/* Addresses of static data and the "loader" section of bootloader IRAM, all defined in ld script */
|
||||||
|
const char *reason = NULL;
|
||||||
|
extern int _dram_start, _dram_end, _loader_text_start, _loader_text_end;
|
||||||
|
void *load_addr_p = (void *)load_addr;
|
||||||
|
void *load_end_p = (void *)load_end;
|
||||||
|
|
||||||
|
if (load_end == load_addr) {
|
||||||
|
return true; // zero-length segments are fine
|
||||||
|
}
|
||||||
|
assert(load_end > load_addr); // data_len<16MB is checked in verify_segment_header() which is called before this, so this should always be true
|
||||||
|
|
||||||
|
if (esp_ptr_in_dram(load_addr_p) && esp_ptr_in_dram(load_end_p)) { /* Writing to DRAM */
|
||||||
|
/* Check if we're clobbering the stack */
|
||||||
|
intptr_t sp = (intptr_t)get_sp();
|
||||||
|
if (bootloader_util_regions_overlap(sp - STACK_LOAD_HEADROOM, SOC_ROM_STACK_START,
|
||||||
|
load_addr, load_end)) {
|
||||||
|
reason = "overlaps bootloader stack";
|
||||||
|
goto invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we're clobbering static data
|
||||||
|
|
||||||
|
(_dram_start.._dram_end includes bss, data, rodata sections in DRAM)
|
||||||
|
*/
|
||||||
|
if (bootloader_util_regions_overlap((intptr_t)&_dram_start, (intptr_t)&_dram_end, load_addr, load_end)) {
|
||||||
|
reason = "overlaps bootloader data";
|
||||||
|
goto invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LAST DRAM CHECK (recursive): for D/IRAM, check the equivalent IRAM addresses if needed
|
||||||
|
|
||||||
|
Allow for the possibility that even though both pointers are IRAM, only part of the region is in a D/IRAM
|
||||||
|
section. In which case we recurse to check the part which falls in D/IRAM.
|
||||||
|
|
||||||
|
Note: We start with SOC_DIRAM_DRAM_LOW/HIGH and convert that address to IRAM to account for any reversing of word order
|
||||||
|
(chip-specific).
|
||||||
|
*/
|
||||||
|
if (!no_recurse && bootloader_util_regions_overlap(SOC_DIRAM_DRAM_LOW, SOC_DIRAM_DRAM_HIGH, load_addr, load_end)) {
|
||||||
|
intptr_t iram_load_addr, iram_load_end;
|
||||||
|
|
||||||
|
if (esp_ptr_in_diram_dram(load_addr_p)) {
|
||||||
|
iram_load_addr = (intptr_t)esp_ptr_diram_dram_to_iram(load_addr_p);
|
||||||
|
} else {
|
||||||
|
iram_load_addr = (intptr_t)esp_ptr_diram_dram_to_iram((void *)SOC_DIRAM_DRAM_LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_ptr_in_diram_dram(load_end_p)) {
|
||||||
|
iram_load_end = (intptr_t)esp_ptr_diram_dram_to_iram(load_end_p);
|
||||||
|
} else {
|
||||||
|
iram_load_end = (intptr_t)esp_ptr_diram_dram_to_iram((void *)SOC_DIRAM_DRAM_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iram_load_end < iram_load_addr) {
|
||||||
|
return verify_load_addresses(segment_index, iram_load_end, iram_load_addr, print_error, true);
|
||||||
|
} else {
|
||||||
|
return verify_load_addresses(segment_index, iram_load_addr, iram_load_end, print_error, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (esp_ptr_in_iram(load_addr_p) && esp_ptr_in_iram(load_end_p)) { /* Writing to IRAM */
|
||||||
|
/* Check for overlap of 'loader' section of IRAM */
|
||||||
|
if (bootloader_util_regions_overlap((intptr_t)&_loader_text_start, (intptr_t)&_loader_text_end,
|
||||||
|
load_addr, load_end)) {
|
||||||
|
reason = "overlaps loader IRAM";
|
||||||
|
goto invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LAST IRAM CHECK (recursive): for D/IRAM, check the equivalent DRAM address if needed
|
||||||
|
|
||||||
|
Allow for the possibility that even though both pointers are IRAM, only part of the region is in a D/IRAM
|
||||||
|
section. In which case we recurse to check the part which falls in D/IRAM.
|
||||||
|
Note: We start with SOC_DIRAM_IRAM_LOW/HIGH and convert that address to DRAM to account for any reversing of word order
|
||||||
|
(chip-specific).
|
||||||
|
*/
|
||||||
|
if (!no_recurse && bootloader_util_regions_overlap(SOC_DIRAM_IRAM_LOW, SOC_DIRAM_IRAM_HIGH, load_addr, load_end)) {
|
||||||
|
intptr_t dram_load_addr, dram_load_end;
|
||||||
|
|
||||||
|
if (esp_ptr_in_diram_iram(load_addr_p)) {
|
||||||
|
dram_load_addr = (intptr_t)esp_ptr_diram_iram_to_dram(load_addr_p);
|
||||||
|
} else {
|
||||||
|
dram_load_addr = (intptr_t)esp_ptr_diram_iram_to_dram((void *)SOC_DIRAM_IRAM_LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_ptr_in_diram_iram(load_end_p)) {
|
||||||
|
dram_load_end = (intptr_t)esp_ptr_diram_iram_to_dram(load_end_p);
|
||||||
|
} else {
|
||||||
|
dram_load_end = (intptr_t)esp_ptr_diram_iram_to_dram((void *)SOC_DIRAM_IRAM_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dram_load_end < dram_load_addr) {
|
||||||
|
return verify_load_addresses(segment_index, dram_load_end, dram_load_addr, print_error, true);
|
||||||
|
} else {
|
||||||
|
return verify_load_addresses(segment_index, dram_load_addr, dram_load_end, print_error, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Sections entirely in RTC memory won't overlap with a vanilla bootloader but are valid load addresses, thus skipping them from the check */
|
||||||
|
} else if (esp_ptr_in_rtc_iram_fast(load_addr_p) && esp_ptr_in_rtc_iram_fast(load_end_p)){
|
||||||
|
return true;
|
||||||
|
} else if (esp_ptr_in_rtc_dram_fast(load_addr_p) && esp_ptr_in_rtc_dram_fast(load_end_p)){
|
||||||
|
return true;
|
||||||
|
} else if (esp_ptr_in_rtc_slow(load_addr_p) && esp_ptr_in_rtc_slow(load_end_p)) {
|
||||||
|
return true;
|
||||||
|
} else { /* Not a DRAM or an IRAM or RTC Fast IRAM, RTC Fast DRAM or RTC Slow address */
|
||||||
|
reason = "bad load address range";
|
||||||
|
goto invalid;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
invalid:
|
||||||
|
if (print_error) {
|
||||||
|
ESP_LOGE(TAG, "Segment %d 0x%08x-0x%08x invalid: %s", segment_index, load_addr, load_end, reason);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif // BOOTLOADER_BUILD
|
||||||
|
|
||||||
static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segment_header_t *header, bool silent, bool do_load, bootloader_sha256_handle_t sha_handle, uint32_t *checksum)
|
static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segment_header_t *header, bool silent, bool do_load, bootloader_sha256_handle_t sha_handle, uint32_t *checksum)
|
||||||
{
|
{
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
@ -376,33 +523,8 @@ static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segme
|
|||||||
#ifdef BOOTLOADER_BUILD
|
#ifdef BOOTLOADER_BUILD
|
||||||
/* Before loading segment, check it doesn't clobber bootloader RAM. */
|
/* Before loading segment, check it doesn't clobber bootloader RAM. */
|
||||||
if (do_load && data_len > 0) {
|
if (do_load && data_len > 0) {
|
||||||
const intptr_t load_end = load_addr + data_len;
|
if (!verify_load_addresses(index, load_addr, load_addr + data_len, true, false)) {
|
||||||
if (load_end < (intptr_t) SOC_DRAM_HIGH) {
|
return ESP_ERR_IMAGE_INVALID;
|
||||||
/* Writing to DRAM */
|
|
||||||
intptr_t sp = (intptr_t)get_sp();
|
|
||||||
if (load_end > sp - STACK_LOAD_HEADROOM) {
|
|
||||||
/* Bootloader .data/.rodata/.bss is above the stack, so this
|
|
||||||
* also checks that we aren't overwriting these segments.
|
|
||||||
*
|
|
||||||
* TODO: This assumes specific arrangement of sections we have
|
|
||||||
* in the ESP32. Rewrite this in a generic way to support other
|
|
||||||
* layouts.
|
|
||||||
*/
|
|
||||||
ESP_LOGE(TAG, "Segment %d end address 0x%08x too high (bootloader stack 0x%08x limit 0x%08x)",
|
|
||||||
index, load_end, sp, sp - STACK_LOAD_HEADROOM);
|
|
||||||
return ESP_ERR_IMAGE_INVALID;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Writing to IRAM */
|
|
||||||
const intptr_t loader_iram_start = (intptr_t) &_loader_text_start;
|
|
||||||
const intptr_t loader_iram_end = (intptr_t) &_loader_text_end;
|
|
||||||
|
|
||||||
if (bootloader_util_regions_overlap(loader_iram_start, loader_iram_end,
|
|
||||||
load_addr, load_end)) {
|
|
||||||
ESP_LOGE(TAG, "Segment %d (0x%08x-0x%08x) overlaps bootloader IRAM (0x%08x-0x%08x)",
|
|
||||||
index, load_addr, load_end, loader_iram_start, loader_iram_end);
|
|
||||||
return ESP_ERR_IMAGE_INVALID;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // BOOTLOADER_BUILD
|
#endif // BOOTLOADER_BUILD
|
||||||
@ -412,6 +534,10 @@ static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segme
|
|||||||
|
|
||||||
int32_t data_len_remain = data_len;
|
int32_t data_len_remain = data_len;
|
||||||
while (data_len_remain > 0) {
|
while (data_len_remain > 0) {
|
||||||
|
#if SECURE_BOOT_CHECK_SIGNATURE && defined(BOOTLOADER_BUILD)
|
||||||
|
/* Double check the address verification done above */
|
||||||
|
ESP_FAULT_ASSERT(!do_load || verify_load_addresses(0, load_addr, load_addr + data_len_remain, false, false));
|
||||||
|
#endif
|
||||||
uint32_t offset_page = ((data_addr & MMAP_ALIGNED_MASK) != 0) ? 1 : 0;
|
uint32_t offset_page = ((data_addr & MMAP_ALIGNED_MASK) != 0) ? 1 : 0;
|
||||||
/* Data we could map in case we are not aligned to PAGE boundary is one page size lesser. */
|
/* Data we could map in case we are not aligned to PAGE boundary is one page size lesser. */
|
||||||
data_len = MIN(data_len_remain, ((free_page_count - offset_page) * SPI_FLASH_MMU_PAGE_SIZE));
|
data_len = MIN(data_len_remain, ((free_page_count - offset_page) * SPI_FLASH_MMU_PAGE_SIZE));
|
||||||
@ -437,7 +563,7 @@ static esp_err_t process_segment_data(intptr_t load_addr, uint32_t data_addr, ui
|
|||||||
{
|
{
|
||||||
// If we are not loading, and the checksum is empty, skip processing this
|
// If we are not loading, and the checksum is empty, skip processing this
|
||||||
// segment for data
|
// segment for data
|
||||||
if(!do_load && checksum == NULL) {
|
if (!do_load && checksum == NULL) {
|
||||||
ESP_LOGD(TAG, "skipping checksum for segment");
|
ESP_LOGD(TAG, "skipping checksum for segment");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
@ -620,9 +746,9 @@ static esp_err_t verify_checksum(bootloader_sha256_handle_t sha_handle, uint32_t
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data)
|
static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data, uint8_t *image_digest, uint8_t *verified_digest)
|
||||||
{
|
{
|
||||||
uint8_t image_hash[HASH_LEN] = { 0 };
|
#ifdef SECURE_BOOT_CHECK_SIGNATURE
|
||||||
uint32_t end = data->start_addr + data->image_len;
|
uint32_t end = data->start_addr + data->image_len;
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Verifying image signature...");
|
ESP_LOGI(TAG, "Verifying image signature...");
|
||||||
@ -634,21 +760,37 @@ static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_han
|
|||||||
bootloader_sha256_data(sha_handle, simple_hash, HASH_LEN);
|
bootloader_sha256_data(sha_handle, simple_hash, HASH_LEN);
|
||||||
bootloader_munmap(simple_hash);
|
bootloader_munmap(simple_hash);
|
||||||
}
|
}
|
||||||
bootloader_sha256_finish(sha_handle, image_hash);
|
|
||||||
|
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||||
|
// End of the image needs to be padded all the way to a 4KB boundary, after the simple hash
|
||||||
|
// (for apps they are usually already padded due to --secure-pad-v2, only a problem if this option was not used.)
|
||||||
|
uint32_t padded_end = (end + FLASH_SECTOR_SIZE - 1) & ~(FLASH_SECTOR_SIZE-1);
|
||||||
|
if (padded_end > end) {
|
||||||
|
const void *padding = bootloader_mmap(end, padded_end - end);
|
||||||
|
bootloader_sha256_data(sha_handle, padding, padded_end - end);
|
||||||
|
bootloader_munmap(padding);
|
||||||
|
end = padded_end;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bootloader_sha256_finish(sha_handle, image_digest);
|
||||||
|
|
||||||
// Log the hash for debugging
|
// Log the hash for debugging
|
||||||
bootloader_debug_buffer(image_hash, HASH_LEN, "Calculated secure boot hash");
|
bootloader_debug_buffer(image_digest, HASH_LEN, "Calculated secure boot hash");
|
||||||
|
|
||||||
#ifdef SECURE_BOOT_CHECK_SIGNATURE
|
|
||||||
// Use hash to verify signature block
|
// Use hash to verify signature block
|
||||||
esp_err_t err = ESP_ERR_IMAGE_INVALID;
|
esp_err_t err = ESP_ERR_IMAGE_INVALID;
|
||||||
const void *sig_block;
|
const void *sig_block;
|
||||||
#ifdef CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
#ifdef CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||||
|
ESP_FAULT_ASSERT(memcmp(image_digest, verified_digest, HASH_LEN) != 0); /* sanity check that these values start differently */
|
||||||
sig_block = bootloader_mmap(data->start_addr + data->image_len, sizeof(esp_secure_boot_sig_block_t));
|
sig_block = bootloader_mmap(data->start_addr + data->image_len, sizeof(esp_secure_boot_sig_block_t));
|
||||||
#elif CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
err = esp_secure_boot_verify_ecdsa_signature_block(sig_block, image_digest, verified_digest);
|
||||||
|
#elif CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||||
|
ESP_FAULT_ASSERT(memcmp(image_digest, verified_digest, HASH_LEN) != 0); /* sanity check that these values start differently */
|
||||||
sig_block = bootloader_mmap(end, sizeof(ets_secure_boot_signature_t));
|
sig_block = bootloader_mmap(end, sizeof(ets_secure_boot_signature_t));
|
||||||
#endif
|
err = esp_secure_boot_verify_rsa_signature_block(sig_block, image_digest, verified_digest);
|
||||||
err = esp_secure_boot_verify_signature_block(sig_block, image_hash);
|
#endif
|
||||||
|
|
||||||
bootloader_munmap(sig_block);
|
bootloader_munmap(sig_block);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Secure boot signature verification failed");
|
ESP_LOGE(TAG, "Secure boot signature verification failed");
|
||||||
@ -668,13 +810,13 @@ static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_han
|
|||||||
}
|
}
|
||||||
return ESP_ERR_IMAGE_INVALID;
|
return ESP_ERR_IMAGE_INVALID;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||||
// Adjust image length result to include the appended signature
|
// Adjust image length result to include the appended signature
|
||||||
data->image_len = end - data->start_addr + sizeof(ets_secure_boot_signature_t);
|
data->image_len = end - data->start_addr + sizeof(ets_secure_boot_signature_t);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif // SECURE_BOOT_CHECK_SIGNATURE
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ extern const uint8_t signature_verification_key_end[] asm("_binary_signature_ver
|
|||||||
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
||||||
{
|
{
|
||||||
uint8_t digest[DIGEST_LEN];
|
uint8_t digest[DIGEST_LEN];
|
||||||
|
uint8_t verified_digest[DIGEST_LEN];
|
||||||
const esp_secure_boot_sig_block_t *sigblock;
|
const esp_secure_boot_sig_block_t *sigblock;
|
||||||
|
|
||||||
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
|
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
|
||||||
@ -57,12 +58,12 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
|||||||
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr + length, sizeof(esp_secure_boot_sig_block_t));
|
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr + length, sizeof(esp_secure_boot_sig_block_t));
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
err = esp_secure_boot_verify_signature_block(sigblock, digest);
|
err = esp_secure_boot_verify_ecdsa_signature_block(sigblock, digest, verified_digest);
|
||||||
bootloader_munmap(sigblock);
|
bootloader_munmap(sigblock);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest)
|
esp_err_t esp_secure_boot_verify_ecdsa_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest)
|
||||||
{
|
{
|
||||||
#if !(defined(CONFIG_MBEDTLS_ECDSA_C) && defined(CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED))
|
#if !(defined(CONFIG_MBEDTLS_ECDSA_C) && defined(CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED))
|
||||||
ESP_LOGE(TAG, "Signature verification requires ECDSA & SECP256R1 curve enabled");
|
ESP_LOGE(TAG, "Signature verification requires ECDSA & SECP256R1 curve enabled");
|
||||||
@ -70,6 +71,9 @@ esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block
|
|||||||
#else
|
#else
|
||||||
ptrdiff_t keylen;
|
ptrdiff_t keylen;
|
||||||
|
|
||||||
|
/* Note: in IDF app image verification we don't add any fault injection resistance, boot-time checks only */
|
||||||
|
memset(verified_digest, 0, DIGEST_LEN);
|
||||||
|
|
||||||
keylen = signature_verification_key_end - signature_verification_key_start;
|
keylen = signature_verification_key_end - signature_verification_key_start;
|
||||||
if (keylen != SIGNATURE_VERIFICATION_KEYLEN) {
|
if (keylen != SIGNATURE_VERIFICATION_KEYLEN) {
|
||||||
ESP_LOGE(TAG, "Embedded public verification key has wrong length %d", keylen);
|
ESP_LOGE(TAG, "Embedded public verification key has wrong length %d", keylen);
|
||||||
@ -141,9 +145,10 @@ static const char *TAG = "secure_boot_v2";
|
|||||||
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
||||||
{
|
{
|
||||||
uint8_t digest[DIGEST_LEN] = {0};
|
uint8_t digest[DIGEST_LEN] = {0};
|
||||||
|
uint8_t verified_digest[DIGEST_LEN] = {0};
|
||||||
|
|
||||||
/* Rounding off length to the upper 4k boundary */
|
/* Rounding off length to the upper 4k boundary */
|
||||||
int padded_length = ALIGN_UP(length, FLASH_SECTOR_SIZE);
|
uint32_t padded_length = ALIGN_UP(length, FLASH_SECTOR_SIZE);
|
||||||
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
|
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
|
||||||
|
|
||||||
esp_err_t err = bootloader_sha256_flash_contents(src_addr, padded_length, digest);
|
esp_err_t err = bootloader_sha256_flash_contents(src_addr, padded_length, digest);
|
||||||
@ -158,7 +163,7 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
|||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = esp_secure_boot_verify_signature_block(sig_block, digest);
|
err = esp_secure_boot_verify_rsa_signature_block(sig_block, digest, verified_digest);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Secure Boot V2 verification failed.");
|
ESP_LOGE(TAG, "Secure Boot V2 verification failed.");
|
||||||
}
|
}
|
||||||
@ -166,11 +171,15 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t esp_secure_boot_verify_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest)
|
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)
|
||||||
{
|
{
|
||||||
uint8_t i = 0, efuse_trusted_digest[DIGEST_LEN] = {0}, sig_block_trusted_digest[DIGEST_LEN] = {0};
|
uint8_t i = 0, 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));
|
memcpy(efuse_trusted_digest, (uint8_t *) EFUSE_BLK2_RDATA0_REG, sizeof(efuse_trusted_digest));
|
||||||
|
|
||||||
|
/* 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 */
|
/* Generating the SHA of the public key components in the signature block */
|
||||||
bootloader_sha256_handle_t sig_block_sha;
|
bootloader_sha256_handle_t sig_block_sha;
|
||||||
sig_block_sha = bootloader_sha256_start();
|
sig_block_sha = bootloader_sha256_start();
|
||||||
|
93
components/esp_common/include/esp_fault.h
Normal file
93
components/esp_common/include/esp_fault.h
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// 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 "sdkconfig.h"
|
||||||
|
#include "soc/rtc_cntl_reg.h"
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Assert a condition is true, in a way that should be resistant to fault injection for
|
||||||
|
* single fault attacks.
|
||||||
|
*
|
||||||
|
* - Expands CONDITION multiple times (condition must have no side effects)
|
||||||
|
* - Compiler is told all registers are invalid before evaluating CONDITION each time, to avoid a fault
|
||||||
|
* causing a misread of a register used in all three evaluations of CONDITION.
|
||||||
|
* - If CONDITION is ever false, a system reset is triggered.
|
||||||
|
*
|
||||||
|
* @note Place this macro after a "normal" check of CONDITION that will fail with a normal error
|
||||||
|
* message. This is the fallback in case a fault injection attack skips or corrupts the result of
|
||||||
|
* that check. (Although ensure that an attacker can't use fault injection to skip past the "normal"
|
||||||
|
* error message, to avoid this check entirely.)
|
||||||
|
*
|
||||||
|
* @note This macro increases binary size and is slow and should be used sparingly.
|
||||||
|
*
|
||||||
|
* @note This macro does not guarantee fault injection resistance. In particular CONDITION must be
|
||||||
|
* chosen carefully - a fault injection attack which sets CONDITION to true will not be detected by
|
||||||
|
* this macro. Care must also be taken that an attacker can't use a fault to completely bypass calling
|
||||||
|
* whatever function tests ESP_FAULT_ASSERT.
|
||||||
|
*
|
||||||
|
* @note This is difficult to debug as a failure triggers an instant software reset, and UART output
|
||||||
|
* is often truncated (as FIFO is not flushed). Define the ESP_FAULT_ASSERT_DEBUG macro to debug any
|
||||||
|
* failures of this macro due to software bugs.
|
||||||
|
*
|
||||||
|
* @param CONDITION A condition which will evaluate true unless an attacker used fault injection to skip or corrupt some other critical system calculation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define ESP_FAULT_ASSERT(CONDITION) do { \
|
||||||
|
asm volatile ("" ::: "memory"); \
|
||||||
|
if(!(CONDITION)) _ESP_FAULT_RESET(); \
|
||||||
|
asm volatile ("" ::: "memory"); \
|
||||||
|
if(!(CONDITION)) _ESP_FAULT_RESET(); \
|
||||||
|
asm volatile ("" ::: "memory"); \
|
||||||
|
if(!(CONDITION)) _ESP_FAULT_RESET(); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
|
||||||
|
// Uncomment this macro to get debug output if ESP_FAULT_ASSERT() fails
|
||||||
|
//
|
||||||
|
// Note that uncommenting this macro reduces the anti-FI effectiveness
|
||||||
|
//
|
||||||
|
//#define ESP_FAULT_ASSERT_DEBUG
|
||||||
|
|
||||||
|
/* Internal macro, purpose is to trigger a system reset if an inconsistency due to fault injection
|
||||||
|
is detected.
|
||||||
|
|
||||||
|
Illegal instruction opcodes are there as a fallback to crash the CPU in case it doesn't
|
||||||
|
reset as expected.
|
||||||
|
*/
|
||||||
|
#ifndef ESP_FAULT_ASSERT_DEBUG
|
||||||
|
|
||||||
|
#define _ESP_FAULT_RESET() do { \
|
||||||
|
REG_WRITE(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST); \
|
||||||
|
asm volatile("ill; ill; ill;"); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#else // ESP_FAULT_ASSERT_DEBUG
|
||||||
|
|
||||||
|
#warning "Enabling ESP_FAULT_ASSERT_DEBUG makes ESP_FAULT_ASSERT() less effective"
|
||||||
|
|
||||||
|
#define _ESP_FAULT_RESET() do { \
|
||||||
|
ets_printf("ESP_FAULT_ASSERT %s:%d\n", __FILE__, __LINE__); \
|
||||||
|
asm volatile("ill;"); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#endif // ESP_FAULT_ASSERT_DEBUG
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
|
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -29,45 +29,77 @@ typedef struct ets_secure_boot_sig_block ets_secure_boot_sig_block_t;
|
|||||||
typedef struct ets_secure_boot_signature ets_secure_boot_signature_t;
|
typedef struct ets_secure_boot_signature ets_secure_boot_signature_t;
|
||||||
typedef struct ets_secure_boot_key_digests ets_secure_boot_key_digests_t;
|
typedef struct ets_secure_boot_key_digests ets_secure_boot_key_digests_t;
|
||||||
|
|
||||||
/* Verify bootloader image (reconfigures cache to map,
|
/* 64KB 'staging buffer' for loading the verified bootloader
|
||||||
loads trusted key digests from efuse)
|
|
||||||
|
|
||||||
If allow_key_revoke is true and aggressive revoke efuse is set,
|
Comes from the "shared buffers" region (see shared_buffers.h)
|
||||||
any failed signature has its associated key revoked in efuse.
|
|
||||||
|
|
||||||
If result is ETS_OK, the "simple hash" of the bootloader
|
The bootloader can't be safely linked into this address range
|
||||||
is copied into verified_hash.
|
(may be possible with some cleverness.)
|
||||||
*/
|
*/
|
||||||
int ets_secure_boot_verify_bootloader(uint8_t *verified_hash, bool allow_key_revoke);
|
#define SECURE_BOOT_STAGING_BUFFER_START ((uint32_t)(g_shared_buffers.secure_boot_staging_buf))
|
||||||
|
#define SECURE_BOOT_STAGING_BUFFER_SZ sizeof(g_shared_buffers.secure_boot_staging_buf)
|
||||||
|
#define SECURE_BOOT_STAGING_BUFFER_END (SECURE_BOOT_STAGING_BUFFER_START + SECURE_BOOT_STAGING_BUFFER_SZ)
|
||||||
|
|
||||||
/* Verify bootloader image (reconfigures cache to map), with
|
/* Anti-FI measure: use full words for success/fail, instead of
|
||||||
key digests provided as parameters.)
|
0/non-zero
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SB_SUCCESS = 0x3A5A5AA5,
|
||||||
|
SB_FAILED = 0x7533885E,
|
||||||
|
} ets_secure_boot_status_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* Verify and stage-load the bootloader image
|
||||||
|
(reconfigures cache to map, loads trusted key digests from efuse,
|
||||||
|
copies the bootloader into the staging buffer.)
|
||||||
|
|
||||||
|
If allow_key_revoke is true and aggressive revoke efuse is set,
|
||||||
|
any failed signature has its associated key revoked in efuse.
|
||||||
|
|
||||||
|
If result is SB_SUCCESS, the "simple hash" of the bootloader
|
||||||
|
is copied into verified_hash.
|
||||||
|
*/
|
||||||
|
ets_secure_boot_status_t ets_secure_boot_verify_stage_bootloader(uint8_t *verified_hash, bool allow_key_revoke);
|
||||||
|
|
||||||
|
/* Verify bootloader image (reconfigures cache to map),
|
||||||
|
with key digests provided as parameters.)
|
||||||
|
|
||||||
Can be used to verify secure boot status before enabling
|
Can be used to verify secure boot status before enabling
|
||||||
secure boot permanently.
|
secure boot permanently.
|
||||||
|
|
||||||
If result is ETS_OK, the "simple hash" of the bootloader is
|
If stage_load parameter is true, bootloader is copied into staging
|
||||||
|
buffer in RAM at the same time.
|
||||||
|
|
||||||
|
If result is SB_SUCCESS, the "simple hash" of the bootloader is
|
||||||
copied into verified_hash.
|
copied into verified_hash.
|
||||||
*/
|
*/
|
||||||
int ets_secure_boot_verify_bootloader_with_keys(uint8_t *verified_hash, const ets_secure_boot_key_digests_t *trusted_keys);
|
ets_secure_boot_status_t ets_secure_boot_verify_bootloader_with_keys(uint8_t *verified_hash, const ets_secure_boot_key_digests_t *trusted_keys, bool stage_load);
|
||||||
|
|
||||||
|
/* Read key digests from efuse. Any revoked/missing digests will be
|
||||||
|
marked as NULL
|
||||||
|
*/
|
||||||
|
ETS_STATUS ets_secure_boot_read_key_digests(ets_secure_boot_key_digests_t *trusted_keys);
|
||||||
|
|
||||||
/* Verify supplied signature against supplied digest, using
|
/* Verify supplied signature against supplied digest, using
|
||||||
supplied trusted key digests.
|
supplied trusted key digests.
|
||||||
|
|
||||||
Doesn't reconfigure cache or any other hardware access.
|
Doesn't reconfigure cache or any other hardware access except for RSA peripheral.
|
||||||
*/
|
|
||||||
int ets_secure_boot_verify_signature(const ets_secure_boot_signature_t *sig, const uint8_t *image_digest, const ets_secure_boot_key_digests_t *trusted_keys);
|
|
||||||
|
|
||||||
/* Read key digests from efuse. Any revoked/missing digests will be
|
If result is SB_SUCCESS, the image_digest value is copied into verified_digest.
|
||||||
marked as NULL
|
|
||||||
|
|
||||||
Returns 0 if at least one valid digest was found.
|
|
||||||
*/
|
*/
|
||||||
int ets_secure_boot_read_key_digests(ets_secure_boot_key_digests_t *trusted_keys);
|
ets_secure_boot_status_t ets_secure_boot_verify_signature(const ets_secure_boot_signature_t *sig, const uint8_t *image_digest, const ets_secure_boot_key_digests_t *trusted_keys, uint8_t *verified_digest);
|
||||||
|
|
||||||
|
/* Revoke a public key digest in efuse.
|
||||||
|
@param index Digest to revoke. Must be 0, 1 or 2.
|
||||||
|
*/
|
||||||
|
void ets_secure_boot_revoke_public_key_digest(int index);
|
||||||
|
|
||||||
#define ETS_SECURE_BOOT_V2_SIGNATURE_MAGIC 0xE7
|
#define ETS_SECURE_BOOT_V2_SIGNATURE_MAGIC 0xE7
|
||||||
|
|
||||||
/* Secure Boot V2 signature block (up to 3 can be appended) */
|
/* Secure Boot V2 signature block
|
||||||
|
|
||||||
|
(Up to 3 in a signature sector are appended to the image)
|
||||||
|
*/
|
||||||
struct ets_secure_boot_sig_block {
|
struct ets_secure_boot_sig_block {
|
||||||
uint8_t magic_byte;
|
uint8_t magic_byte;
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
@ -92,8 +124,10 @@ struct ets_secure_boot_signature {
|
|||||||
|
|
||||||
_Static_assert(sizeof(ets_secure_boot_signature_t) == 4096, "invalid sig sector size");
|
_Static_assert(sizeof(ets_secure_boot_signature_t) == 4096, "invalid sig sector size");
|
||||||
|
|
||||||
|
#define MAX_KEY_DIGESTS 3
|
||||||
|
|
||||||
struct ets_secure_boot_key_digests {
|
struct ets_secure_boot_key_digests {
|
||||||
const void *key_digests[3];
|
const void *key_digests[MAX_KEY_DIGESTS];
|
||||||
bool allow_key_revoke;
|
bool allow_key_revoke;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,17 +39,16 @@ possible. This should optimize the amount of RAM accessible to the code without
|
|||||||
IRAM_ATTR static void *dram_alloc_to_iram_addr(void *addr, size_t len)
|
IRAM_ATTR static void *dram_alloc_to_iram_addr(void *addr, size_t len)
|
||||||
{
|
{
|
||||||
uintptr_t dstart = (uintptr_t)addr; //First word
|
uintptr_t dstart = (uintptr_t)addr; //First word
|
||||||
uintptr_t dend = dstart + len; //Last word + 4
|
uintptr_t dend = dstart + len - 4; //Last word
|
||||||
assert(esp_ptr_in_diram_dram((void *)dstart));
|
assert(esp_ptr_in_diram_dram((void *)dstart));
|
||||||
assert(esp_ptr_in_diram_dram((void *)dend));
|
assert(esp_ptr_in_diram_dram((void *)dend));
|
||||||
assert((dstart & 3) == 0);
|
assert((dstart & 3) == 0);
|
||||||
assert((dend & 3) == 0);
|
assert((dend & 3) == 0);
|
||||||
#if SOC_DIRAM_INVERTED
|
#ifdef SOC_DIRAM_INVERTED // We want the word before the result to hold the DRAM address
|
||||||
uint32_t istart = SOC_DIRAM_IRAM_LOW + (SOC_DIRAM_DRAM_HIGH - dend);
|
uint32_t *iptr = esp_ptr_diram_dram_to_iram((void *)dend);
|
||||||
#else
|
#else
|
||||||
uint32_t istart = SOC_DIRAM_IRAM_LOW + (dstart - SOC_DIRAM_DRAM_LOW);
|
uint32_t *iptr = esp_ptr_diram_dram_to_iram((void *)dstart);
|
||||||
#endif
|
#endif
|
||||||
uint32_t *iptr = (uint32_t *)istart;
|
|
||||||
*iptr = dstart;
|
*iptr = dstart;
|
||||||
return iptr + 1;
|
return iptr + 1;
|
||||||
}
|
}
|
||||||
|
74
components/heap/test/test_diram.c
Normal file
74
components/heap/test/test_diram.c
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
Tests for D/IRAM support in heap capability allocator
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <esp_types.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "unity.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
#include "soc/soc_memory_layout.h"
|
||||||
|
|
||||||
|
#define ALLOC_SZ 1024
|
||||||
|
|
||||||
|
static void *malloc_block_diram(uint32_t caps)
|
||||||
|
{
|
||||||
|
void *attempts[256] = { 0 }; // Allocate up to 256 ALLOC_SZ blocks to exhaust all non-D/IRAM memory temporarily
|
||||||
|
int count = 0;
|
||||||
|
void *result;
|
||||||
|
|
||||||
|
while(count < sizeof(attempts)/sizeof(void *)) {
|
||||||
|
result = heap_caps_malloc(ALLOC_SZ, caps);
|
||||||
|
TEST_ASSERT_NOT_NULL_MESSAGE(result, "not enough free heap to perform test");
|
||||||
|
|
||||||
|
if (esp_ptr_in_diram_dram(result) || esp_ptr_in_diram_iram(result)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
attempts[count] = result;
|
||||||
|
result = NULL;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
free(attempts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_ASSERT_NOT_NULL_MESSAGE(result, "not enough D/IRAM memory is free");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Allocate D/IRAM as DRAM", "[heap]")
|
||||||
|
{
|
||||||
|
uint32_t *dram = malloc_block_diram(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||||
|
|
||||||
|
for (int i = 0; i < ALLOC_SZ / sizeof(uint32_t); i++) {
|
||||||
|
uint32_t v = i + 0xAAAA;
|
||||||
|
dram[i] = v;
|
||||||
|
volatile uint32_t *iram = esp_ptr_diram_dram_to_iram(dram + i);
|
||||||
|
TEST_ASSERT_EQUAL(v, dram[i]);
|
||||||
|
TEST_ASSERT_EQUAL(v, *iram);
|
||||||
|
*iram = UINT32_MAX;
|
||||||
|
TEST_ASSERT_EQUAL(UINT32_MAX, *iram);
|
||||||
|
TEST_ASSERT_EQUAL(UINT32_MAX, dram[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(dram);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Allocate D/IRAM as IRAM", "[heap]")
|
||||||
|
{
|
||||||
|
uint32_t *iram = malloc_block_diram(MALLOC_CAP_EXEC);
|
||||||
|
|
||||||
|
for (int i = 0; i < ALLOC_SZ / sizeof(uint32_t); i++) {
|
||||||
|
uint32_t v = i + 0xEEE;
|
||||||
|
iram[i] = v;
|
||||||
|
volatile uint32_t *dram = esp_ptr_diram_iram_to_dram(iram + i);
|
||||||
|
TEST_ASSERT_EQUAL_HEX32(v, iram[i]);
|
||||||
|
TEST_ASSERT_EQUAL_HEX32(v, *dram);
|
||||||
|
*dram = UINT32_MAX;
|
||||||
|
TEST_ASSERT_EQUAL_HEX32(UINT32_MAX, *dram);
|
||||||
|
TEST_ASSERT_EQUAL_HEX32(UINT32_MAX, iram[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(iram);
|
||||||
|
}
|
@ -213,9 +213,47 @@ inline static bool IRAM_ATTR esp_ptr_in_diram_iram(const void *p) {
|
|||||||
return ((intptr_t)p >= SOC_DIRAM_IRAM_LOW && (intptr_t)p < SOC_DIRAM_IRAM_HIGH);
|
return ((intptr_t)p >= SOC_DIRAM_IRAM_LOW && (intptr_t)p < SOC_DIRAM_IRAM_HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline static bool IRAM_ATTR esp_ptr_in_rtc_iram_fast(const void *p) {
|
||||||
|
return ((intptr_t)p >= SOC_RTC_IRAM_LOW && (intptr_t)p < SOC_RTC_IRAM_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static bool IRAM_ATTR esp_ptr_in_rtc_dram_fast(const void *p) {
|
||||||
|
return ((intptr_t)p >= SOC_RTC_DRAM_LOW && (intptr_t)p < SOC_RTC_DRAM_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static bool IRAM_ATTR esp_ptr_in_rtc_slow(const void *p) {
|
||||||
|
return ((intptr_t)p >= SOC_RTC_DATA_LOW && (intptr_t)p < SOC_RTC_DATA_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert a D/IRAM DRAM pointer to equivalent word address in IRAM
|
||||||
|
|
||||||
|
- Address must be word aligned
|
||||||
|
- Address must pass esp_ptr_in_diram_dram() test, or result will be invalid pointer
|
||||||
|
*/
|
||||||
|
inline static void * IRAM_ATTR esp_ptr_diram_dram_to_iram(const void *p) {
|
||||||
|
#if SOC_DIRAM_INVERTED
|
||||||
|
return (void *) ( SOC_DIRAM_IRAM_LOW + (SOC_DIRAM_DRAM_HIGH - (intptr_t)p) - 4);
|
||||||
|
#else
|
||||||
|
return (void *) ( SOC_DIRAM_IRAM_LOW + ((intptr_t)p - SOC_DIRAM_DRAM_LOW) );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert a D/IRAM IRAM pointer to equivalent word address in DRAM
|
||||||
|
|
||||||
|
- Address must be word aligned
|
||||||
|
- Address must pass esp_ptr_in_diram_iram() test, or result will be invalid pointer
|
||||||
|
*/
|
||||||
|
inline static void * IRAM_ATTR esp_ptr_diram_iram_to_dram(const void *p) {
|
||||||
|
#if SOC_DIRAM_INVERTED
|
||||||
|
return (void *) ( SOC_DIRAM_DRAM_LOW + (SOC_DIRAM_IRAM_HIGH - (intptr_t)p) - 4);
|
||||||
|
#else
|
||||||
|
return (void *) ( SOC_DIRAM_DRAM_LOW + ((intptr_t)p - SOC_DIRAM_IRAM_LOW) );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
inline static bool IRAM_ATTR esp_stack_ptr_is_sane(uint32_t sp)
|
inline static bool IRAM_ATTR esp_stack_ptr_is_sane(uint32_t sp)
|
||||||
{
|
{
|
||||||
//Check if stack ptr is in between SOC_DRAM_LOW and SOC_DRAM_HIGH, and 16 byte aligned.
|
//Check if stack ptr is in between SOC_DRAM_LOW and SOC_DRAM_HIGH, and 16 byte aligned.
|
||||||
return !(sp < SOC_DRAM_LOW + 0x10 || sp > SOC_DRAM_HIGH - 0x10 || ((sp & 0xF) != 0));
|
return !(sp < SOC_DRAM_LOW + 0x10 || sp > SOC_DRAM_HIGH - 0x10 || ((sp & 0xF) != 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,6 +277,9 @@
|
|||||||
#define SOC_MEM_INTERNAL_LOW 0x3FF90000
|
#define SOC_MEM_INTERNAL_LOW 0x3FF90000
|
||||||
#define SOC_MEM_INTERNAL_HIGH 0x400C2000
|
#define SOC_MEM_INTERNAL_HIGH 0x400C2000
|
||||||
|
|
||||||
|
// Start (highest address) of ROM boot stack, only relevant during early boot
|
||||||
|
#define SOC_ROM_STACK_START 0x3ffe3f20
|
||||||
|
|
||||||
//Interrupt hardware source table
|
//Interrupt hardware source table
|
||||||
//This table is decided by hardware, don't touch this.
|
//This table is decided by hardware, don't touch this.
|
||||||
#define ETS_WIFI_MAC_INTR_SOURCE 0/**< interrupt of WiFi MAC, level*/
|
#define ETS_WIFI_MAC_INTR_SOURCE 0/**< interrupt of WiFi MAC, level*/
|
||||||
|
@ -283,6 +283,9 @@
|
|||||||
#define SOC_MEM_INTERNAL_LOW 0x3FF9E000
|
#define SOC_MEM_INTERNAL_LOW 0x3FF9E000
|
||||||
#define SOC_MEM_INTERNAL_HIGH 0x40072000
|
#define SOC_MEM_INTERNAL_HIGH 0x40072000
|
||||||
|
|
||||||
|
// Start (highest address) of ROM boot stack, only relevant during early boot
|
||||||
|
#define SOC_ROM_STACK_START 0x3fffe70c
|
||||||
|
|
||||||
//interrupt cpu using table, Please see the core-isa.h
|
//interrupt cpu using table, Please see the core-isa.h
|
||||||
/*************************************************************************************************************
|
/*************************************************************************************************************
|
||||||
* Intr num Level Type PRO CPU usage APP CPU uasge
|
* Intr num Level Type PRO CPU usage APP CPU uasge
|
||||||
|
@ -336,7 +336,7 @@ example_test_012:
|
|||||||
|
|
||||||
UT_001:
|
UT_001:
|
||||||
extends: .unit_test_template
|
extends: .unit_test_template
|
||||||
parallel: 34
|
parallel: 36
|
||||||
tags:
|
tags:
|
||||||
- ESP32_IDF
|
- ESP32_IDF
|
||||||
- UT_T1_1
|
- UT_T1_1
|
||||||
|
Loading…
Reference in New Issue
Block a user