Merge branch 'backport/anti-fi-check-sbv1-v3.3.3' into 'release/v3.3'

bootloader: Add fault injection resistance to Secure Boot bootloader verification

See merge request espressif/esp-idf!12738
This commit is contained in:
Angus Gratton 2021-04-12 23:53:05 +00:00
commit 63d482e162
14 changed files with 580 additions and 79 deletions

View File

@ -75,6 +75,7 @@ SECTIONS
.dram0.bss (NOLOAD) :
{
. = ALIGN (8);
_dram_start = ABSOLUTE(.);
_bss_start = ABSOLUTE(.);
*(.dynsbss)
*(.sbss)
@ -151,7 +152,7 @@ SECTIONS
*(.gnu.linkonce.lit4.*)
_lit4_end = ABSOLUTE(.);
. = ALIGN(4);
_heap_start = ABSOLUTE(.);
_dram_end = ABSOLUTE(.);
} >dram_seg
.iram.text :

View File

@ -123,6 +123,19 @@ typedef struct {
esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest);
/** @brief Verify the ECDSA secure boot signature block for Secure Boot.
*
* Calculates Deterministic ECDSA w/ SHA256 based on the SHA256 hash of the image. ECDSA signature
* verification must be enabled in project configuration to use this function.
*
* Similar to esp_secure_boot_verify_signature(), but can be used when the digest is precalculated.
* @param sig_block Pointer to ECDSA 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_ecdsa_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest);
#define FLASH_OFFS_SECURE_BOOT_IV_DIGEST 0
/** @brief Secure boot IV+digest header */

View File

@ -14,6 +14,7 @@
#pragma once
#include "esp_image_format.h"
#include "bootloader_config.h"
/**
* @brief Load partition table.

View File

@ -18,6 +18,7 @@
#include <soc/cpu.h>
#include <esp_image_format.h>
#include <esp_secure_boot.h>
#include <esp_fault.h>
#include <esp_log.h>
#include <esp_spi_flash.h>
#include <bootloader_flash.h>
@ -25,6 +26,7 @@
#include <bootloader_sha.h>
#include "bootloader_util.h"
#include "bootloader_common.h"
#include "soc/soc_memory_layout.h"
/* Checking signatures as part of verifying images is necessary:
- Always if secure boot is enabled
@ -92,7 +94,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 __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 image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data)
@ -108,6 +110,12 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
uint32_t checksum_word = ESP_ROM_CHECKSUM_INITIAL;
bootloader_sha256_handle_t sha_handle = NULL;
#ifdef 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) {
return ESP_ERR_INVALID_ARG;
}
@ -196,7 +204,7 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
if (!is_bootloader) {
#ifdef SECURE_BOOT_CHECK_SIGNATURE
// 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
// No secure boot, but SHA-256 can be appended for basic corruption detection
if (sha_handle != NULL && !esp_cpu_in_ocd_debug_mode()) {
@ -226,7 +234,22 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
}
#ifdef BOOTLOADER_BUILD
if (do_load) { // 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.
*/
ESP_FAULT_ASSERT(data->start_addr == ESP_BOOTLOADER_OFFSET || memcmp(image_digest, verified_digest, HASH_LEN) == 0);
#endif // SECURE_BOOT_CHECK_SIGNATURE
if (do_load && ram_obfs_value[0] != 0 && ram_obfs_value[1] != 0) { // Need to deobfuscate RAM
for (int i = 0; i < data->image.segment_count; i++) {
uint32_t load_addr = data->segments[i].load_addr;
if (should_load(load_addr)) {
@ -298,6 +321,127 @@ static esp_err_t verify_image_header(uint32_t src_addr, const esp_image_header_t
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)
{
esp_err_t err;
@ -337,37 +481,11 @@ static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segme
(do_load)?"load":(is_mapping)?"map":"");
}
#ifdef BOOTLOADER_BUILD
/* Before loading segment, check it doesn't clobber bootloader RAM. */
if (do_load) {
const intptr_t load_end = load_addr + data_len;
if (load_end <= (intptr_t) SOC_DIRAM_DRAM_HIGH) {
/* 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;
}
if (do_load && data_len > 0) {
if (!verify_load_addresses(index, load_addr, load_addr + data_len, true, false)) {
return ESP_ERR_IMAGE_INVALID;
}
}
#endif // BOOTLOADER_BUILD
@ -377,6 +495,10 @@ static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segme
int32_t data_len_remain = data_len;
while (data_len_remain > 0) {
#if defined(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;
/* 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));
@ -572,28 +694,33 @@ static esp_err_t verify_checksum(bootloader_sha256_handle_t sha_handle, uint32_t
static void debug_log_hash(const uint8_t *image_hash, const char *caption);
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;
ESP_LOGI(TAG, "Verifying image signature...");
// For secure boot, we calculate the signature hash over the whole file, which includes any "simple" hash
// appended to the image for corruption detection
if (data->image.hash_appended) {
const void *simple_hash = bootloader_mmap(data->start_addr + data->image_len - HASH_LEN, HASH_LEN);
const void *simple_hash = bootloader_mmap(end - HASH_LEN, HASH_LEN);
bootloader_sha256_data(sha_handle, simple_hash, HASH_LEN);
bootloader_munmap(simple_hash);
}
bootloader_sha256_finish(sha_handle, image_hash);
bootloader_sha256_finish(sha_handle, image_digest);
// Log the hash for debugging
debug_log_hash(image_hash, "Calculated secure boot hash");
debug_log_hash(image_digest, "Calculated secure boot hash");
// Use hash to verify signature block
const esp_secure_boot_sig_block_t *sig_block = bootloader_mmap(data->start_addr + data->image_len, sizeof(esp_secure_boot_sig_block_t));
esp_err_t err = esp_secure_boot_verify_signature_block(sig_block, image_hash);
esp_err_t err = ESP_ERR_IMAGE_INVALID;
const void *sig_block;
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));
err = esp_secure_boot_verify_ecdsa_signature_block(sig_block, image_digest, verified_digest);
bootloader_munmap(sig_block);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Secure boot signature verification failed");
@ -614,6 +741,7 @@ static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_han
return ESP_ERR_IMAGE_INVALID;
}
#endif // SECURE_BOOT_CHECK_SIGNATURE
return ESP_OK;
}

View File

@ -15,11 +15,22 @@
#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 "uECC.h"
#ifdef BOOTLOADER_BUILD
#include "uECC_verify_antifault.h"
#else
#include "mbedtls/sha256.h"
#include "mbedtls/x509.h"
#include "mbedtls/md.h"
#include "mbedtls/platform.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include <string.h>
#endif
#include <sys/param.h>
@ -38,38 +49,17 @@ 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)
{
uint8_t digest[DIGEST_LEN];
const uint8_t *data;
uint8_t verified_digest[DIGEST_LEN] = { 0 }; /* ignored in this function */
const esp_secure_boot_sig_block_t *sigblock;
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
bootloader_sha256_handle_t handle = bootloader_sha256_start();
esp_err_t err = bootloader_sha256_flash_contents(src_addr, length, digest);
uint32_t free_page_count = bootloader_mmap_get_free_pages();
ESP_LOGD(TAG, "free data page_count 0x%08x", free_page_count);
int32_t data_len_remain = length;
uint32_t data_addr = src_addr;
while (data_len_remain > 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. */
uint32_t data_len = MIN(data_len_remain, ((free_page_count - offset_page) * SPI_FLASH_MMU_PAGE_SIZE));
data = (const uint8_t *) bootloader_mmap(data_addr, data_len);
if(!data) {
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", data_addr, data_len);
bootloader_sha256_finish(handle, NULL);
return ESP_FAIL;
}
bootloader_sha256_data(handle, data, data_len);
bootloader_munmap(data);
data_addr += data_len;
data_len_remain -= data_len;
if (err != ESP_OK) {
return err;
}
/* Done! Get the digest */
bootloader_sha256_finish(handle, digest);
// Map the signature block
sigblock = (const esp_secure_boot_sig_block_t *) bootloader_mmap(src_addr + length, sizeof(esp_secure_boot_sig_block_t));
if(!sigblock) {
@ -77,20 +67,27 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
return ESP_FAIL;
}
// Verify the signature
esp_err_t err = esp_secure_boot_verify_signature_block(sigblock, digest);
err = esp_secure_boot_verify_ecdsa_signature_block(sigblock, digest, verified_digest);
// Unmap
bootloader_munmap(sigblock);
return err;
}
#ifdef BOOTLOADER_BUILD
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;
bool is_valid;
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);
return ESP_FAIL;
}
@ -102,11 +99,88 @@ esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block
ESP_LOGD(TAG, "Verifying secure boot signature");
is_valid = uECC_verify(signature_verification_key_start,
is_valid = uECC_verify_antifault(signature_verification_key_start,
image_digest,
DIGEST_LEN,
sig_block->signature,
uECC_secp256r1());
uECC_secp256r1(),
verified_digest);
ESP_LOGD(TAG, "Verification result %d", is_valid);
return is_valid ? ESP_OK : ESP_ERR_IMAGE_INVALID;
}
#else // BOOTLOADER_BUILD
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))
ESP_LOGE(TAG, "Signature verification requires ECDSA & SECP256R1 curve enabled");
return ESP_ERR_NOT_SUPPORTED;
#else
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;
if (keylen != SIGNATURE_VERIFICATION_KEYLEN) {
ESP_LOGE(TAG, "Embedded public verification key has wrong length %d", keylen);
return ESP_FAIL;
}
if (sig_block->version != 0) {
ESP_LOGE(TAG, "image has invalid signature version field 0x%08x", sig_block->version);
return ESP_FAIL;
}
ESP_LOGD(TAG, "Verifying secure boot signature");
int ret;
mbedtls_mpi r, s;
mbedtls_mpi_init(&r);
mbedtls_mpi_init(&s);
/* Extract r and s components from RAW ECDSA signature of 64 bytes */
#define ECDSA_INTEGER_LEN 32
ret = mbedtls_mpi_read_binary(&r, &sig_block->signature[0], ECDSA_INTEGER_LEN);
if (ret != 0) {
ESP_LOGE(TAG, "Failed mbedtls_mpi_read_binary(1), err:%d", ret);
return ESP_FAIL;
}
ret = mbedtls_mpi_read_binary(&s, &sig_block->signature[ECDSA_INTEGER_LEN], ECDSA_INTEGER_LEN);
if (ret != 0) {
ESP_LOGE(TAG, "Failed mbedtls_mpi_read_binary(2), err:%d", ret);
mbedtls_mpi_free(&r);
return ESP_FAIL;
}
/* Initialise ECDSA context */
mbedtls_ecdsa_context ecdsa_context;
mbedtls_ecdsa_init(&ecdsa_context);
mbedtls_ecp_group_load(&ecdsa_context.grp, MBEDTLS_ECP_DP_SECP256R1);
size_t plen = mbedtls_mpi_size(&ecdsa_context.grp.P);
if (keylen != 2 * plen) {
ESP_LOGE(TAG, "Incorrect ECDSA key length %d", keylen);
ret = ESP_FAIL;
goto cleanup;
}
/* Extract X and Y components from ECDSA public key */
MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&ecdsa_context.Q.X, signature_verification_key_start, plen));
MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&ecdsa_context.Q.Y, signature_verification_key_start + plen, plen));
MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&ecdsa_context.Q.Z, 1));
ret = mbedtls_ecdsa_verify(&ecdsa_context.grp, image_digest, DIGEST_LEN, &ecdsa_context.Q, &r, &s);
ESP_LOGD(TAG, "Verification result %d", ret);
cleanup:
mbedtls_mpi_free(&r);
mbedtls_mpi_free(&s);
mbedtls_ecdsa_free(&ecdsa_context);
return ret == 0 ? ESP_OK : ESP_ERR_IMAGE_INVALID;
#endif // CONFIG_MBEDTLS_ECDSA_C && CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED
}
#endif // BOOTLOADER_BUILD

View File

@ -0,0 +1,94 @@
// 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

View File

@ -57,7 +57,7 @@ ifndef IS_BOOTLOADER_BUILD
APP_BIN_UNSIGNED := $(APP_BIN:.bin=-unsigned.bin)
$(APP_BIN): $(APP_BIN_UNSIGNED) $(SECURE_BOOT_SIGNING_KEY) $(SDKCONFIG_MAKEFILE)
$(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $<
$(ESPSECUREPY) sign_data --version 1 --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $<
endif
endif
# non-secure boot (or bootloader), both these files are the same

View File

@ -1,7 +1,7 @@
# only compile the "micro-ecc/uECC.c" source file
set(COMPONENT_SRCS "micro-ecc/uECC.c")
# only compile the "uECC_verify_antifault.c" file which includes the "micro-ecc/uECC.c" source file
set(COMPONENT_SRCS "uECC_verify_antifault.c")
set(COMPONENT_ADD_INCLUDEDIRS micro-ecc)
set(COMPONENT_ADD_INCLUDEDIRS . micro-ecc)
set(COMPONENT_REQUIRES)

View File

@ -1,8 +1,7 @@
# only compile the micro-ecc/uECC.c source file
# (SRCDIRS is needed so build system can find the source file)
COMPONENT_SRCDIRS := micro-ecc
COMPONENT_OBJS := micro-ecc/uECC.o
COMPONENT_SRCDIRS := .
COMPONENT_ADD_INCLUDEDIRS := micro-ecc
COMPONENT_ADD_INCLUDEDIRS := . micro-ecc
COMPONENT_SUBMODULES := micro-ecc

View File

@ -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 (unsigned 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));
}

View File

@ -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);

View File

@ -50,7 +50,7 @@ ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES
PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN:.bin=-unsigned.bin)
# add an extra signing step for secure partition table
$(PARTITION_TABLE_BIN): $(PARTITION_TABLE_BIN_UNSIGNED) $(SDKCONFIG_MAKEFILE) $(SECURE_BOOT_SIGNING_KEY)
$(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $<
$(ESPSECUREPY) sign_data --version 1 --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $<
else
# secure bootloader disabled, both files are the same
PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN)

View File

@ -322,6 +322,9 @@
#define SOC_MEM_INTERNAL_LOW 0x3FF90000
#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
//This table is decided by hardware, don't touch this.
#define ETS_WIFI_MAC_INTR_SOURCE 0/**< interrupt of WiFi MAC, level*/

View File

@ -207,9 +207,38 @@ 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);
}
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.
return !(sp < SOC_DRAM_LOW + 0x10 || sp > SOC_DRAM_HIGH - 0x10 || ((sp & 0xF) != 0));
}
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) {
return (void *) ( SOC_DIRAM_IRAM_LOW + ((intptr_t)p - SOC_DIRAM_DRAM_LOW) );
}
/* 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) {
return (void *) ( SOC_DIRAM_DRAM_LOW + ((intptr_t)p - SOC_DIRAM_IRAM_LOW) );
}