]> granicus.if.org Git - esp-idf/commitdiff
build system: include SHA256 hash of ELF file into app_desc structure
authorIvan Grokhotkov <ivan@espressif.com>
Wed, 9 Jan 2019 12:06:01 +0000 (20:06 +0800)
committerIvan Grokhotkov <ivan@espressif.com>
Wed, 27 Feb 2019 05:30:25 +0000 (13:30 +0800)
components/app_update/esp_app_desc.c
components/app_update/include/esp_ota_ops.h
components/app_update/test/test_app_desc.c [new file with mode: 0644]
components/esp32/cpu_start.c
components/esptool_py/Makefile.projbuild
components/esptool_py/project_include.cmake
examples/get-started/blink/example_test.py [new file with mode: 0644]

index 20c487c1c7216da8bf375329f7a1cfbfa822fa75..23d3b4cc7201e33559904efbecc9a856b8ed3751 100644 (file)
@@ -13,7 +13,9 @@
 // limitations under the License.
 
 #include <assert.h>
+#include <sys/param.h>
 #include "esp_ota_ops.h"
+#include "esp_attr.h"
 #include "sdkconfig.h"
 
 // Application version info
@@ -60,3 +62,24 @@ const esp_app_desc_t *esp_ota_get_app_description(void)
 {
     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;
+}
index 94a34b4f98ded4205cda40f54648770f2b689b6f..635144638dde40a00ea09fa52d3c77f22e8ceea3 100644 (file)
@@ -55,6 +55,16 @@ typedef uint32_t esp_ota_handle_t;
  */
 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.
 
diff --git a/components/app_update/test/test_app_desc.c b/components/app_update/test/test_app_desc.c
new file mode 100644 (file)
index 0000000..3187294
--- /dev/null
@@ -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]);
+}
index d2f7a6317706536ffbe809e0a09ce7b3d010ec7e..bf28857fb8fa0242403c1e7531063c6309191911 100644 (file)
@@ -189,9 +189,11 @@ void IRAM_ATTR call_start_cpu0()
         ESP_EARLY_LOGI(TAG, "Secure version:   %d", app_desc->secure_version);
 #endif
 #ifdef CONFIG_APP_COMPILE_TIME_DATE
-        ESP_EARLY_LOGI(TAG, "Compile time:     %s", app_desc->time);
-        ESP_EARLY_LOGI(TAG, "Compile date:     %s", app_desc->date);
+        ESP_EARLY_LOGI(TAG, "Compile time:     %s %s", app_desc->date, app_desc->time);
 #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);
     }
 
index c3ce445dddadcdb2fc5732d7b3491713a0fa00cd..25f2487ab46405bb282324cf7bd2307686a22440 100644 (file)
@@ -39,6 +39,8 @@ 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)
 
 ESPTOOL_ALL_FLASH_ARGS += $(APP_OFFSET) $(APP_BIN)
index 83e129ae646ffbf2e68fc2cc8251b04adb52e851..ff6da5b8a2acd3aa7f092c24223371962ea2b6b6 100644 (file)
@@ -52,6 +52,8 @@ if(CONFIG_SECURE_BOOT_ENABLED AND
         ${ESPTOOLPY_ELF2IMAGE_FLASH_OPTIONS} --secure-pad)
 endif()
 
+set(ESPTOOLPY_ELF2IMAGE_OPTIONS --elf-sha256-offset 0xb0)
+
 if(CONFIG_ESPTOOLPY_FLASHSIZE_DETECT)
     # Set ESPFLASHSIZE to 'detect' *after* elf2image options are generated,
     # as elf2image can't have 'detect' as an option...
@@ -75,7 +77,7 @@ endif()
 # Add 'app.bin' target - generates with elf2image
 #
 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}"
     DEPENDS ${IDF_PROJECT_EXECUTABLE}
     VERBATIM
diff --git a/examples/get-started/blink/example_test.py b/examples/get-started/blink/example_test.py
new file mode 100644 (file)
index 0000000..91b0f9b
--- /dev/null
@@ -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()