bootloader: Obfuscate loaded memory until verification is complete

This commit is contained in:
Angus Gratton 2017-06-21 17:39:15 +10:00
parent 0c8888d68f
commit 8f6134dd96

View File

@ -18,6 +18,7 @@
#include <esp_image_format.h>
#include <esp_log.h>
#include <bootloader_flash.h>
#include <bootloader_random.h>
static const char *TAG = "esp_image";
@ -27,6 +28,13 @@ static const char *TAG = "esp_image";
/* Headroom to ensure between stack SP (at time of checking) and data loaded from flash */
#define STACK_LOAD_HEADROOM 4096
#ifdef BOOTLOADER_BUILD
/* 64 bits of random data to obfuscate loaded RAM with, until verification is complete
(Means loaded code isn't executable until after the secure boot check.)
*/
static uint32_t ram_obfs_value[2];
#endif
/* Return true if load_addr is an address the bootloader should load into */
static bool should_load(uint32_t load_addr);
/* Return true if load_addr is an address the bootloader should map via flash cache */
@ -98,7 +106,7 @@ goto err;
}
uint32_t next_addr = data->start_addr + sizeof(esp_image_header_t);
for(int i = 0; i < data->image.segment_count && err == ESP_OK; i++) {
for(int i = 0; i < data->image.segment_count; i++) {
esp_image_segment_header_t *header = &data->segments[i];
ESP_LOGV(TAG, "loading segment header %d at offset 0x%x", i, next_addr);
err = process_segment(i, next_addr, header, silent, do_load, &checksum_word);
@ -138,6 +146,20 @@ goto err;
data->image_length = length;
#ifdef BOOTLOADER_BUILD
if (do_load) { // 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)) {
uint32_t *loaded = (uint32_t *)load_addr;
for (int j = 0; j < data->segments[i].data_len/sizeof(uint32_t); j++) {
loaded[j] ^= (j & 1) ? ram_obfs_value[0] : ram_obfs_value[1];
}
}
}
}
#endif
// Success!
return ESP_OK;
@ -222,24 +244,31 @@ static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segme
}
}
const void *data = bootloader_mmap(data_addr, data_len);
const uint32_t *data = (const uint32_t *)bootloader_mmap(data_addr, data_len);
if(!data) {
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed",
data_addr, data_len);
return ESP_FAIL;
}
const uint32_t *checksum_from;
if (do_load) {
memcpy((void *)load_addr, data, data_len);
checksum_from = (const uint32_t *)load_addr;
} else {
checksum_from = (const uint32_t *)data;
#ifdef BOOTLOADER_BUILD
// Set up the obfuscation value to use for loading
while (ram_obfs_value[0] == 0 || ram_obfs_value[1] == 0) {
bootloader_fill_random(ram_obfs_value, sizeof(ram_obfs_value));
}
// Update checksum, either from RAM we just loaded or from flash
for (const uint32_t *c = checksum_from;
c < checksum_from + (data_len/sizeof(uint32_t));
c++) {
*checksum ^= *c;
uint32_t *dest = (uint32_t *)load_addr;
#endif
const uint32_t *src = data;
for (int i = 0; i < data_len/sizeof(uint32_t); i++) {
uint32_t w = src[i];
*checksum ^= w;
#ifdef BOOTLOADER_BUILD
if (do_load) {
dest[i] = w ^ ((i & 1) ? ram_obfs_value[0] : ram_obfs_value[1]);
}
#endif
}
bootloader_munmap(data);
@ -264,13 +293,14 @@ static esp_err_t verify_segment_header(int index, const esp_image_segment_header
}
uint32_t load_addr = segment->load_addr;
bool map_segment = should_map(load_addr);
/* Check that flash cache mapped segment aligns correctly from flash to its mapped address,
relative to the 64KB page mapping size.
*/
ESP_LOGV(TAG, "segment %d map_segment %d segment_data_offs 0x%x load_addr 0x%x",
index, map_segment, segment_data_offs, load_addr);
if (should_map(load_addr)
if (map_segment
&& ((segment_data_offs % SPI_FLASH_MMU_PAGE_SIZE) != (load_addr % SPI_FLASH_MMU_PAGE_SIZE))) {
if (!silent) {
ESP_LOGE(TAG, "Segment %d has load address 0x%08x, doesn't match segment data at 0x%08x",