// 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
{
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;
+}
*/
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.
--- /dev/null
+#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]);
+}
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);
}
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)
${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...
# 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
--- /dev/null
+#!/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()