forked from espressif/esp-idf
Merge branch 'feature/embed_elf_sha256' into 'master'
build system: include SHA256 hash of ELF file into app_desc structure See merge request idf/esp-idf!4093
This commit is contained in:
@@ -13,7 +13,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <sys/param.h>
|
||||||
#include "esp_ota_ops.h"
|
#include "esp_ota_ops.h"
|
||||||
|
#include "esp_attr.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
// Application version info
|
// Application version info
|
||||||
@@ -60,3 +62,24 @@ const esp_app_desc_t *esp_ota_get_app_description(void)
|
|||||||
{
|
{
|
||||||
return &esp_app_desc;
|
return &esp_app_desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The following two functions may be called from the panic handler
|
||||||
|
* or core dump, hence IRAM_ATTR.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline char IRAM_ATTR to_hex_digit(unsigned val)
|
||||||
|
{
|
||||||
|
return (val < 10) ? ('0' + val) : ('a' + val - 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
int IRAM_ATTR esp_ota_get_app_elf_sha256(char* dst, size_t size)
|
||||||
|
{
|
||||||
|
size_t n = MIN((size - 1) / 2, sizeof(esp_app_desc.app_elf_sha256));
|
||||||
|
const uint8_t* src = esp_app_desc.app_elf_sha256;
|
||||||
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
dst[2*i] = to_hex_digit(src[i] >> 4);
|
||||||
|
dst[2*i + 1] = to_hex_digit(src[i] & 0xf);
|
||||||
|
}
|
||||||
|
dst[2*n] = 0;
|
||||||
|
return 2*n + 1;
|
||||||
|
}
|
||||||
|
@@ -55,6 +55,16 @@ typedef uint32_t esp_ota_handle_t;
|
|||||||
*/
|
*/
|
||||||
const esp_app_desc_t *esp_ota_get_app_description(void);
|
const esp_app_desc_t *esp_ota_get_app_description(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fill the provided buffer with SHA256 of the ELF file, formatted as hexadecimal, null-terminated.
|
||||||
|
* If the buffer size is not sufficient to fit the entire SHA256 in hex plus a null terminator,
|
||||||
|
* the largest possible number of bytes will be written followed by a null.
|
||||||
|
* @param dst Destination buffer
|
||||||
|
* @param size Size of the buffer
|
||||||
|
* @return Number of bytes written to dst (including null terminator)
|
||||||
|
*/
|
||||||
|
int esp_ota_get_app_elf_sha256(char* dst, size_t size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Commence an OTA update writing to the specified partition.
|
* @brief Commence an OTA update writing to the specified partition.
|
||||||
|
|
||||||
|
50
components/app_update/test/test_app_desc.c
Normal file
50
components/app_update/test/test_app_desc.c
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include "esp_ota_ops.h"
|
||||||
|
#include "unity.h"
|
||||||
|
|
||||||
|
TEST_CASE("esp_ota_get_app_elf_sha256 test", "[esp_app_desc]")
|
||||||
|
{
|
||||||
|
const int sha256_hex_len = 64;
|
||||||
|
char dst[sha256_hex_len + 2];
|
||||||
|
const char fill = 0xcc;
|
||||||
|
int res;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
char ref_sha256[sha256_hex_len + 1];
|
||||||
|
const esp_app_desc_t* desc = esp_ota_get_app_description();
|
||||||
|
for (int i = 0; i < sizeof(ref_sha256) / 2; ++i) {
|
||||||
|
snprintf(ref_sha256 + 2*i, 3, "%02x", desc->app_elf_sha256[i]);
|
||||||
|
}
|
||||||
|
ref_sha256[sha256_hex_len] = 0;
|
||||||
|
|
||||||
|
printf("Ref: %s\n", ref_sha256);
|
||||||
|
|
||||||
|
memset(dst, fill, sizeof(dst));
|
||||||
|
len = sizeof(dst);
|
||||||
|
res = esp_ota_get_app_elf_sha256(dst, len);
|
||||||
|
printf("%d: %s (%d)\n", len, dst, res);
|
||||||
|
TEST_ASSERT_EQUAL(sha256_hex_len + 1, res);
|
||||||
|
TEST_ASSERT_EQUAL(0, memcmp(dst, ref_sha256, res - 1));
|
||||||
|
TEST_ASSERT_EQUAL_HEX(0, dst[sha256_hex_len]);
|
||||||
|
TEST_ASSERT_EQUAL_HEX(fill, dst[sha256_hex_len + 1]);
|
||||||
|
|
||||||
|
memset(dst, fill, sizeof(dst));
|
||||||
|
len = 9;
|
||||||
|
res = esp_ota_get_app_elf_sha256(dst, len);
|
||||||
|
printf("%d: %s (%d)\n", len, dst, res);
|
||||||
|
TEST_ASSERT_EQUAL(9, res);
|
||||||
|
TEST_ASSERT_EQUAL(0, memcmp(dst, ref_sha256, res - 1));
|
||||||
|
TEST_ASSERT_EQUAL_HEX(0, dst[8]);
|
||||||
|
TEST_ASSERT_EQUAL_HEX(fill, dst[9]);
|
||||||
|
|
||||||
|
memset(dst, fill, sizeof(dst));
|
||||||
|
len = 8;
|
||||||
|
res = esp_ota_get_app_elf_sha256(dst, len);
|
||||||
|
printf("%d: %s (%d)\n", len, dst, res);
|
||||||
|
// should output even number of characters plus '\0'
|
||||||
|
TEST_ASSERT_EQUAL(7, res);
|
||||||
|
TEST_ASSERT_EQUAL(0, memcmp(dst, ref_sha256, res - 1));
|
||||||
|
TEST_ASSERT_EQUAL_HEX(0, dst[6]);
|
||||||
|
TEST_ASSERT_EQUAL_HEX(fill, dst[7]);
|
||||||
|
TEST_ASSERT_EQUAL_HEX(fill, dst[8]);
|
||||||
|
}
|
@@ -189,9 +189,11 @@ void IRAM_ATTR call_start_cpu0()
|
|||||||
ESP_EARLY_LOGI(TAG, "Secure version: %d", app_desc->secure_version);
|
ESP_EARLY_LOGI(TAG, "Secure version: %d", app_desc->secure_version);
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_APP_COMPILE_TIME_DATE
|
#ifdef CONFIG_APP_COMPILE_TIME_DATE
|
||||||
ESP_EARLY_LOGI(TAG, "Compile time: %s", app_desc->time);
|
ESP_EARLY_LOGI(TAG, "Compile time: %s %s", app_desc->date, app_desc->time);
|
||||||
ESP_EARLY_LOGI(TAG, "Compile date: %s", app_desc->date);
|
|
||||||
#endif
|
#endif
|
||||||
|
char buf[17];
|
||||||
|
esp_ota_get_app_elf_sha256(buf, sizeof(buf));
|
||||||
|
ESP_EARLY_LOGI(TAG, "ELF file SHA256: %s...", buf);
|
||||||
ESP_EARLY_LOGI(TAG, "ESP-IDF: %s", app_desc->idf_ver);
|
ESP_EARLY_LOGI(TAG, "ESP-IDF: %s", app_desc->idf_ver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -42,6 +42,7 @@
|
|||||||
#include "esp_app_trace.h"
|
#include "esp_app_trace.h"
|
||||||
#include "esp_system_internal.h"
|
#include "esp_system_internal.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_ota_ops.h"
|
||||||
#if CONFIG_SYSVIEW_ENABLE
|
#if CONFIG_SYSVIEW_ENABLE
|
||||||
#include "SEGGER_RTT.h"
|
#include "SEGGER_RTT.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -473,7 +474,7 @@ static void doBacktrace(XtExcFrame *frame)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panicPutStr("\r\n\r\n");
|
panicPutStr("\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -541,9 +542,16 @@ static void commonErrorHandler_dump(XtExcFrame *frame, int core_id)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
panicPutStr("\r\nELF file SHA256: ");
|
||||||
|
char sha256_buf[65];
|
||||||
|
esp_ota_get_app_elf_sha256(sha256_buf, sizeof(sha256_buf));
|
||||||
|
panicPutStr(sha256_buf);
|
||||||
|
panicPutStr("\r\n");
|
||||||
|
|
||||||
/* With windowed ABI backtracing is easy, let's do it. */
|
/* With windowed ABI backtracing is easy, let's do it. */
|
||||||
doBacktrace(frame);
|
doBacktrace(frame);
|
||||||
|
|
||||||
|
panicPutStr("\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -39,6 +39,8 @@ endif
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ESPTOOL_ELF2IMAGE_OPTIONS += --elf-sha256-offset 0xb0
|
||||||
|
|
||||||
ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z,-u) $(ESPTOOL_WRITE_FLASH_OPTIONS)
|
ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z,-u) $(ESPTOOL_WRITE_FLASH_OPTIONS)
|
||||||
|
|
||||||
ESPTOOL_ALL_FLASH_ARGS += $(APP_OFFSET) $(APP_BIN)
|
ESPTOOL_ALL_FLASH_ARGS += $(APP_OFFSET) $(APP_BIN)
|
||||||
|
@@ -52,6 +52,8 @@ if(CONFIG_SECURE_BOOT_ENABLED AND
|
|||||||
${ESPTOOLPY_ELF2IMAGE_FLASH_OPTIONS} --secure-pad)
|
${ESPTOOLPY_ELF2IMAGE_FLASH_OPTIONS} --secure-pad)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(ESPTOOLPY_ELF2IMAGE_OPTIONS --elf-sha256-offset 0xb0)
|
||||||
|
|
||||||
if(CONFIG_ESPTOOLPY_FLASHSIZE_DETECT)
|
if(CONFIG_ESPTOOLPY_FLASHSIZE_DETECT)
|
||||||
# Set ESPFLASHSIZE to 'detect' *after* elf2image options are generated,
|
# Set ESPFLASHSIZE to 'detect' *after* elf2image options are generated,
|
||||||
# as elf2image can't have 'detect' as an option...
|
# as elf2image can't have 'detect' as an option...
|
||||||
@@ -75,7 +77,7 @@ endif()
|
|||||||
# Add 'app.bin' target - generates with elf2image
|
# Add 'app.bin' target - generates with elf2image
|
||||||
#
|
#
|
||||||
add_custom_command(OUTPUT "${IDF_BUILD_ARTIFACTS_DIR}/${unsigned_project_binary}"
|
add_custom_command(OUTPUT "${IDF_BUILD_ARTIFACTS_DIR}/${unsigned_project_binary}"
|
||||||
COMMAND ${ESPTOOLPY} elf2image ${ESPTOOLPY_ELF2IMAGE_FLASH_OPTIONS}
|
COMMAND ${ESPTOOLPY} elf2image ${ESPTOOLPY_ELF2IMAGE_FLASH_OPTIONS} ${ESPTOOLPY_ELF2IMAGE_OPTIONS}
|
||||||
-o "${IDF_BUILD_ARTIFACTS_DIR}/${unsigned_project_binary}" "${IDF_PROJECT_EXECUTABLE}"
|
-o "${IDF_BUILD_ARTIFACTS_DIR}/${unsigned_project_binary}" "${IDF_PROJECT_EXECUTABLE}"
|
||||||
DEPENDS ${IDF_PROJECT_EXECUTABLE}
|
DEPENDS ${IDF_PROJECT_EXECUTABLE}
|
||||||
VERBATIM
|
VERBATIM
|
||||||
|
55
examples/get-started/blink/example_test.py
Normal file
55
examples/get-started/blink/example_test.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from __future__ import division
|
||||||
|
from __future__ import print_function
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
try:
|
||||||
|
import IDF
|
||||||
|
except ImportError:
|
||||||
|
# This environment variable is expected on the host machine
|
||||||
|
test_fw_path = os.getenv("TEST_FW_PATH")
|
||||||
|
if test_fw_path and test_fw_path not in sys.path:
|
||||||
|
sys.path.insert(0, test_fw_path)
|
||||||
|
|
||||||
|
import IDF
|
||||||
|
|
||||||
|
import Utility
|
||||||
|
|
||||||
|
|
||||||
|
def verify_elf_sha256_embedding(dut):
|
||||||
|
elf_file = os.path.join(dut.app.binary_path, "blink.elf")
|
||||||
|
sha256 = hashlib.sha256()
|
||||||
|
with open(elf_file, "rb") as f:
|
||||||
|
sha256.update(f.read())
|
||||||
|
sha256_expected = sha256.hexdigest()
|
||||||
|
|
||||||
|
dut.reset()
|
||||||
|
sha256_reported = dut.expect(re.compile(r'ELF file SHA256:\s+([a-f0-9]+)'), timeout=5)[0]
|
||||||
|
|
||||||
|
Utility.console_log('ELF file SHA256: %s' % sha256_expected)
|
||||||
|
Utility.console_log('ELF file SHA256 (reported by the app): %s' % sha256_reported)
|
||||||
|
# the app reports only the first several hex characters of the SHA256, check that they match
|
||||||
|
if not sha256_expected.startswith(sha256_reported):
|
||||||
|
raise ValueError('ELF file SHA256 mismatch')
|
||||||
|
|
||||||
|
|
||||||
|
@IDF.idf_example_test(env_tag="Example_WIFI")
|
||||||
|
def test_examples_blink(env, extra_data):
|
||||||
|
dut = env.get_dut("blink", "examples/get-started/blink")
|
||||||
|
binary_file = os.path.join(dut.app.binary_path, "blink.bin")
|
||||||
|
bin_size = os.path.getsize(binary_file)
|
||||||
|
IDF.log_performance("blink_bin_size", "{}KB".format(bin_size // 1024))
|
||||||
|
IDF.check_performance("blink_bin_size", bin_size // 1024)
|
||||||
|
|
||||||
|
dut.start_app()
|
||||||
|
|
||||||
|
verify_elf_sha256_embedding(dut)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test_examples_blink()
|
Reference in New Issue
Block a user