From f53fef993623919646c35fae59baa3d9ddc94ae0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 2 Oct 2018 15:15:32 +1000 Subject: [PATCH] Secure Boot & Flash encryption: Support 3/4 Coding Scheme Includes esptool update to v2.6-beta1 --- components/bootloader/Kconfig.projbuild | 21 +++++ components/bootloader/Makefile.projbuild | 10 +- .../bootloader_support/include/esp_efuse.h | 33 +++++++ components/bootloader_support/src/efuse.c | 54 +++++++++++ .../bootloader_support/src/flash_encrypt.c | 14 +-- .../bootloader_support/src/secure_boot.c | 13 +-- .../test/test_efuse_coding_scheme.c | 94 +++++++++++++++++++ components/esptool_py/esptool | 2 +- components/soc/esp32/include/soc/efuse_reg.h | 3 + docs/en/security/flash-encryption.rst | 19 ++-- docs/en/security/secure-boot.rst | 25 +++-- 11 files changed, 250 insertions(+), 38 deletions(-) create mode 100644 components/bootloader_support/test/test_efuse_coding_scheme.c diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index f0188f19a1..3ecbed93b9 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -290,6 +290,27 @@ config SECURE_BOOT_VERIFICATION_KEY Refer to https://docs.espressif.com/projects/esp-idf/en/latest/security/secure-boot.html before enabling. +choice SECURE_BOOTLOADER_KEY_ENCODING + bool "Hardware Key Encoding" + depends on SECURE_BOOTLOADER_REFLASHABLE + default SECURE_BOOTLOADER_NO_ENCODING + help + + In reflashable secure bootloader mode, a hardware key is derived from the signing key (with SHA-256) and can be written to efuse + with espefuse.py. + + Normally this is a 256-bit key, but if 3/4 Coding Scheme is used on the device then the efuse key is truncated to 192 bits. + + This configuration item doesn't change any firmware code, it only changes the size of key binary which is generated at build time. + +config SECURE_BOOTLOADER_KEY_ENCODING_256BIT + bool "No encoding (256 bit key)" + +config SECURE_BOOTLOADER_KEY_ENCODING_192BIT + bool "3/4 encoding (192 bit key)" + +endchoice + config SECURE_BOOT_INSECURE bool "Allow potentially insecure options" depends on SECURE_BOOT_ENABLED diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index ba8855c274..1bbbf55298 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -78,12 +78,18 @@ else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE # Reflashable secure bootloader # generates a digest binary (bootloader + digest) +ifdef CONFIG_SECURE_BOOTLOADER_KEY_ENCODING_192BIT +KEY_DIGEST_LEN=192 +else +KEY_DIGEST_LEN=256 +endif + BOOTLOADER_DIGEST_BIN := $(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin -SECURE_BOOTLOADER_KEY := $(BOOTLOADER_BUILD_DIR)/secure-bootloader-key.bin +SECURE_BOOTLOADER_KEY := $(BOOTLOADER_BUILD_DIR)/secure-bootloader-key-$(KEY_DIGEST_LEN).bin ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES $(SECURE_BOOTLOADER_KEY): $(SECURE_BOOT_SIGNING_KEY) | check_python_dependencies - $(ESPSECUREPY) digest_private_key -k $< $@ + $(ESPSECUREPY) digest_private_key --keylen $(KEY_DIGEST_LEN) -k $< $@ else $(SECURE_BOOTLOADER_KEY): @echo "No pre-generated key for a reflashable secure bootloader is available, due to signing configuration." diff --git a/components/bootloader_support/include/esp_efuse.h b/components/bootloader_support/include/esp_efuse.h index 2f33b05a98..c094a6ab44 100644 --- a/components/bootloader_support/include/esp_efuse.h +++ b/components/bootloader_support/include/esp_efuse.h @@ -15,6 +15,7 @@ #define _ESP_EFUSE_H #include "soc/efuse_reg.h" +#include "esp_err.h" #ifdef __cplusplus extern "C" { @@ -58,6 +59,38 @@ void esp_efuse_reset(void); */ void esp_efuse_disable_basic_rom_console(void); +/* @brief Encode one or more sets of 6 byte sequences into + * 8 bytes suitable for 3/4 Coding Scheme. + * + * This function is only useful if the CODING_SCHEME efuse + * is set to value 1 for 3/4 Coding Scheme. + * + * @param[in] in_bytes Pointer to a sequence of bytes to encode for 3/4 Coding Scheme. Must have length in_bytes_len. After being written to hardware, these bytes will read back as little-endian words. + * @param[out] out_words Pointer to array of words suitable for writing to efuse write registers. Array must contain 2 words (8 bytes) for every 6 bytes in in_bytes_len. Can be a pointer to efuse write registers. + * @param in_bytes_len. Length of array pointed to by in_bytes, in bytes. Must be a multiple of 6. + * + * @return ESP_ERR_INVALID_ARG if either pointer is null or in_bytes_len is not a multiple of 6. ESP_OK otherwise. + */ +esp_err_t esp_efuse_apply_34_encoding(const uint8_t *in_bytes, uint32_t *out_words, size_t in_bytes_len); + +/* @brief Write random data to efuse key block write registers + * + * @note Caller is responsible for ensuring efuse + * block is empty and not write protected, before calling. + * + * @note Behaviour depends on coding scheme: a 256-bit key is + * generated and written for Coding Scheme "None", a 192-bit key + * is generated, extended to 256-bits by the Coding Scheme, + * and then writtten for 3/4 Coding Scheme. + * + * @note This function does not burn the new values, caller should + * call esp_efuse_burn_new_values() when ready to do this. + * + * @param blk_wdata0_reg Address of the first data write register + * in the block + */ +void esp_efuse_write_random_key(uint32_t blk_wdata0_reg); + #ifdef __cplusplus } #endif diff --git a/components/bootloader_support/src/efuse.c b/components/bootloader_support/src/efuse.c index 40bb6d451e..35d0a64e19 100644 --- a/components/bootloader_support/src/efuse.c +++ b/components/bootloader_support/src/efuse.c @@ -13,6 +13,8 @@ // limitations under the License. #include "esp_efuse.h" #include "esp_log.h" +#include +#include "bootloader_random.h" #define EFUSE_CONF_WRITE 0x5A5A /* efuse_pgm_op_ena, force no rd/wr disable */ #define EFUSE_CONF_READ 0x5AA5 /* efuse_read_op_ena, release force */ @@ -58,3 +60,55 @@ void esp_efuse_disable_basic_rom_console(void) esp_efuse_burn_new_values(); } } + +esp_err_t esp_efuse_apply_34_encoding(const uint8_t *in_bytes, uint32_t *out_words, size_t in_bytes_len) +{ + if (in_bytes == NULL || out_words == NULL || in_bytes_len % 6 != 0) { + return ESP_ERR_INVALID_ARG; + } + + while (in_bytes_len > 0) { + uint8_t out[8]; + uint8_t xor = 0; + uint8_t mul = 0; + for (int i = 0; i < 6; i++) { + xor ^= in_bytes[i]; + mul += (i + 1) * __builtin_popcount(in_bytes[i]); + } + + memcpy(out, in_bytes, 6); // Data bytes + out[6] = xor; + out[7] = mul; + + memcpy(out_words, out, 8); + + in_bytes_len -= 6; + in_bytes += 6; + out_words += 2; + } + + return ESP_OK; +} + +void esp_efuse_write_random_key(uint32_t blk_wdata0_reg) +{ + uint32_t buf[8]; + uint8_t raw[24]; + uint32_t coding_scheme = REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_CODING_SCHEME_M; + + if (coding_scheme == EFUSE_CODING_SCHEME_VAL_NONE) { + bootloader_fill_random(buf, sizeof(buf)); + } else { // 3/4 Coding Scheme + bootloader_fill_random(raw, sizeof(raw)); + esp_err_t r = esp_efuse_apply_34_encoding(raw, buf, sizeof(raw)); + assert(r == ESP_OK); + } + + ESP_LOGV(TAG, "Writing random values to address 0x%08x", blk_wdata0_reg); + for (int i = 0; i < 8; i++) { + ESP_LOGV(TAG, "EFUSE_BLKx_WDATA%d_REG = 0x%08x", i, buf[i]); + REG_WRITE(blk_wdata0_reg + 4*i, buf[i]); + } + bzero(buf, sizeof(buf)); + bzero(raw, sizeof(raw)); +} diff --git a/components/bootloader_support/src/flash_encrypt.c b/components/bootloader_support/src/flash_encrypt.c index 728486862d..814bb39259 100644 --- a/components/bootloader_support/src/flash_encrypt.c +++ b/components/bootloader_support/src/flash_encrypt.c @@ -15,7 +15,6 @@ #include #include "bootloader_flash.h" -#include "bootloader_random.h" #include "esp_image_format.h" #include "esp_flash_encrypt.h" #include "esp_flash_partitions.h" @@ -63,8 +62,9 @@ esp_err_t esp_flash_encrypt_check_and_update(void) static esp_err_t initialise_flash_encryption(void) { - if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_CODING_SCHEME_M) { - ESP_LOGE(TAG, "Flash Encryption is currently not supported on hardware with 3/4 Coding Scheme (CODING_SCHEME efuse set)"); + uint32_t coding_scheme = REG_GET_FIELD(EFUSE_BLK0_RDATA6_REG, EFUSE_CODING_SCHEME); + if (coding_scheme != EFUSE_CODING_SCHEME_VAL_NONE && coding_scheme != EFUSE_CODING_SCHEME_VAL_34) { + ESP_LOGE(TAG, "Unknown/unsupported CODING_SCHEME value 0x%x", coding_scheme); return ESP_ERR_NOT_SUPPORTED; } @@ -85,13 +85,7 @@ static esp_err_t initialise_flash_encryption(void) && REG_READ(EFUSE_BLK1_RDATA6_REG) == 0 && REG_READ(EFUSE_BLK1_RDATA7_REG) == 0) { ESP_LOGI(TAG, "Generating new flash encryption key..."); - uint32_t buf[8]; - bootloader_fill_random(buf, sizeof(buf)); - for (int i = 0; i < 8; i++) { - ESP_LOGV(TAG, "EFUSE_BLK1_WDATA%d_REG = 0x%08x", i, buf[i]); - REG_WRITE(EFUSE_BLK1_WDATA0_REG + 4*i, buf[i]); - } - bzero(buf, sizeof(buf)); + esp_efuse_write_random_key(EFUSE_BLK1_WDATA0_REG); esp_efuse_burn_new_values(); ESP_LOGI(TAG, "Read & write protecting new key..."); diff --git a/components/bootloader_support/src/secure_boot.c b/components/bootloader_support/src/secure_boot.c index 36f9ea8c6c..64bf071803 100644 --- a/components/bootloader_support/src/secure_boot.c +++ b/components/bootloader_support/src/secure_boot.c @@ -110,8 +110,9 @@ esp_err_t esp_secure_boot_permanently_enable(void) { return ESP_OK; } - if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_CODING_SCHEME_M) { - ESP_LOGE(TAG, "Secure Boot is currently not supported on hardware with 3/4 Coding Scheme (CODING_SCHEME efuse set)"); + uint32_t coding_scheme = REG_GET_FIELD(EFUSE_BLK0_RDATA6_REG, EFUSE_CODING_SCHEME); + if (coding_scheme != EFUSE_CODING_SCHEME_VAL_NONE && coding_scheme != EFUSE_CODING_SCHEME_VAL_34) { + ESP_LOGE(TAG, "Unknown/unsupported CODING_SCHEME value 0x%x", coding_scheme); return ESP_ERR_NOT_SUPPORTED; } @@ -137,13 +138,7 @@ esp_err_t esp_secure_boot_permanently_enable(void) { && REG_READ(EFUSE_BLK2_RDATA6_REG) == 0 && REG_READ(EFUSE_BLK2_RDATA7_REG) == 0) { ESP_LOGI(TAG, "Generating new secure boot key..."); - uint32_t buf[8]; - bootloader_fill_random(buf, sizeof(buf)); - for (int i = 0; i < 8; i++) { - ESP_LOGV(TAG, "EFUSE_BLK2_WDATA%d_REG = 0x%08x", i, buf[i]); - REG_WRITE(EFUSE_BLK2_WDATA0_REG + 4*i, buf[i]); - } - bzero(buf, sizeof(buf)); + esp_efuse_write_random_key(EFUSE_BLK2_WDATA0_REG); burn_efuses(); ESP_LOGI(TAG, "Read & write protecting new key..."); REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK2 | EFUSE_RD_DIS_BLK2); diff --git a/components/bootloader_support/test/test_efuse_coding_scheme.c b/components/bootloader_support/test/test_efuse_coding_scheme.c new file mode 100644 index 0000000000..b9d5202f95 --- /dev/null +++ b/components/bootloader_support/test/test_efuse_coding_scheme.c @@ -0,0 +1,94 @@ +#include +#include +#include "esp_efuse.h" +#include "unity.h" + +typedef struct { + uint8_t unencoded[24]; + uint32_t encoded[8]; +} coding_scheme_test_t; + +/* Randomly generated byte strings, encoded and written to ESP32 + using espefuse algorithm, then verified to have no encoding errors + and correct readback. +*/ +static const coding_scheme_test_t coding_scheme_data[] = { + { + .unencoded = { 0x96, 0xa9, 0xab, 0xb2, 0xda, 0xdd, 0x21, 0xd2, 0x35, 0x22, 0xd3, 0x30, 0x3b, 0xf8, 0xcb, 0x77, 0x8d, 0x8d, 0xf4, 0x96, 0x25, 0xc4, 0xb9, 0x94 }, + .encoded = { 0xb2aba996, 0x6821ddda, 0x2235d221, 0x430730d3, 0x77cbf83b, 0x627f8d8d, 0xc42596f4, 0x4dae94b9 }, + }, + { + .unencoded = { 0x0e, 0x6b, 0x1a, 0x1d, 0xa5, 0x9f, 0x24, 0xcf, 0x91, 0x5b, 0xe7, 0xe1, 0x7c, 0x0a, 0x6e, 0xdc, 0x5e, 0x8e, 0xb1, 0xec, 0xd1, 0xf3, 0x75, 0x48 }, + .encoded = { 0x1d1a6b0e, 0x5e589fa5, 0x5b91cf24, 0x6127e1e7, 0xdc6e0a7c, 0x5d148e5e, 0xf3d1ecb1, 0x57424875 }, + }, + { + .unencoded = { 0x0a, 0x79, 0x5a, 0x1c, 0xb1, 0x45, 0x71, 0x2c, 0xb3, 0xda, 0x9e, 0xdc, 0x76, 0x27, 0xf5, 0xca, 0xe7, 0x00, 0x39, 0x95, 0x6c, 0x53, 0xc2, 0x07 }, + .encoded = { 0x1c5a790a, 0x4ac145b1, 0xdab32c71, 0x6476dc9e, 0xcaf52776, 0x4d8900e7, 0x536c9539, 0x495607c2 }, + }, + { + .unencoded = { 0x76, 0x46, 0x88, 0x2d, 0x4c, 0xe1, 0x50, 0x5d, 0xd6, 0x7c, 0x41, 0x15, 0xc6, 0x1f, 0xd4, 0x60, 0x10, 0x15, 0x2a, 0x72, 0x2d, 0x89, 0x93, 0x13 }, + .encoded = { 0x2d884676, 0x4838e14c, 0x7cd65d50, 0x4bf31541, 0x60d41fc6, 0x39681510, 0x892d722a, 0x497c1393 }, + }, + { + .unencoded = { 0x32, 0xbc, 0x40, 0x92, 0x13, 0x37, 0x1a, 0xae, 0xb6, 0x00, 0xed, 0x30, 0xb8, 0x82, 0xee, 0xfc, 0xcf, 0x6d, 0x7f, 0xc5, 0xfa, 0x0e, 0xdd, 0x84 }, + .encoded = { 0x9240bc32, 0x49783713, 0x00b6ae1a, 0x46df30ed, 0xfcee82b8, 0x6e8a6dcf, 0x0efac57f, 0x571784dd }, + }, + { + .unencoded = { 0x29, 0xb3, 0x04, 0x95, 0xf2, 0x3c, 0x81, 0xe6, 0x5a, 0xf3, 0x42, 0x82, 0xd1, 0x79, 0xe2, 0x12, 0xbe, 0xc3, 0xd4, 0x10, 0x63, 0x66, 0x9f, 0xe3 }, + .encoded = { 0x9504b329, 0x51c53cf2, 0xf35ae681, 0x460e8242, 0x12e279d1, 0x5825c3be, 0x666310d4, 0x5ebde39f }, + }, + { + .unencoded = { 0xda, 0xda, 0x71, 0x4a, 0x62, 0x33, 0xdd, 0x31, 0x87, 0xf3, 0x70, 0x12, 0x33, 0x3b, 0x3b, 0xe9, 0xed, 0xc4, 0x6e, 0x6a, 0xc7, 0xd5, 0x85, 0xfc }, + .encoded = { 0x4a71dada, 0x4e6a3362, 0xf38731dd, 0x4bfa1270, 0xe93b3b33, 0x61f3c4ed, 0xd5c76a6e, 0x636ffc85 }, + }, + { + .unencoded = { 0x45, 0x64, 0x51, 0x34, 0x1c, 0x82, 0x81, 0x77, 0xf8, 0x89, 0xb1, 0x15, 0x82, 0x94, 0xdd, 0x64, 0xa2, 0x46, 0x0e, 0xfb, 0x1a, 0x70, 0x4b, 0x9f }, + .encoded = { 0x34516445, 0x39da821c, 0x89f87781, 0x4f2315b1, 0x64dd9482, 0x474b46a2, 0x701afb0e, 0x5e4b9f4b }, + }, + { + .unencoded = { 0x89, 0x87, 0x15, 0xb6, 0x66, 0x34, 0x49, 0x18, 0x8b, 0x7b, 0xb2, 0xf6, 0x96, 0x1e, 0x2e, 0xf1, 0x03, 0x9d, 0x4e, 0x16, 0x32, 0xd6, 0x23, 0x22 }, + .encoded = { 0xb6158789, 0x4eff3466, 0x7b8b1849, 0x63e5f6b2, 0xf12e1e96, 0x54c99d03, 0xd632164e, 0x42bd2223 }, + }, + { + .unencoded = { 0xa7, 0xa0, 0xb5, 0x21, 0xd2, 0xa3, 0x9f, 0x65, 0xa9, 0xeb, 0x72, 0xa2, 0x2e, 0xa6, 0xfb, 0x9c, 0x48, 0x7e, 0x68, 0x08, 0x7a, 0xb1, 0x4f, 0xbc }, + .encoded = { 0x21b5a0a7, 0x4ce2a3d2, 0xeba9659f, 0x5868a272, 0x9cfba62e, 0x5fd97e48, 0xb17a0868, 0x5b58bc4f }, + }, + { + .unencoded = { 0xf7, 0x05, 0xe3, 0x6c, 0xb1, 0x55, 0xcb, 0x2f, 0x8d, 0x3e, 0x0b, 0x2e, 0x3e, 0xb7, 0x02, 0xf5, 0x91, 0xb1, 0xfe, 0x8b, 0x58, 0x50, 0xb2, 0x40 }, + .encoded = { 0x6ce305f7, 0x569955b1, 0x3e8d2fcb, 0x56722e0b, 0xf502b73e, 0x535eb191, 0x50588bfe, 0x3a8f40b2 }, + }, + { + .unencoded = { 0x0f, 0x93, 0xb0, 0xd5, 0x60, 0xba, 0x40, 0x2a, 0x62, 0xa6, 0x92, 0x82, 0xb8, 0x91, 0x2c, 0xd7, 0x23, 0xdc, 0x6f, 0x7f, 0x2f, 0xbe, 0x41, 0xf5 }, + .encoded = { 0xd5b0930f, 0x5123ba60, 0xa6622a40, 0x3bbe8292, 0xd72c91b8, 0x582ddc23, 0xbe2f7f6f, 0x6935f541 }, + }, + { + .unencoded = { 0x7f, 0x0c, 0x99, 0xde, 0xff, 0x2e, 0xd2, 0x1c, 0x48, 0x98, 0x70, 0x85, 0x15, 0x01, 0x2a, 0xfb, 0xcd, 0xf2, 0xa0, 0xf9, 0x0e, 0xbc, 0x9f, 0x0c }, + .encoded = { 0xde990c7f, 0x6fe52eff, 0x98481cd2, 0x3deb8570, 0xfb2a0115, 0x61faf2cd, 0xbc0ef9a0, 0x55780c9f }, + }, + { + .unencoded = { 0x9a, 0x10, 0x92, 0x03, 0x81, 0xfe, 0x41, 0x57, 0x77, 0x02, 0xcb, 0x20, 0x67, 0xa4, 0x97, 0xf3, 0xf8, 0xc7, 0x0d, 0x65, 0xcd, 0xfc, 0x15, 0xef }, + .encoded = { 0x0392109a, 0x4b64fe81, 0x02775741, 0x418820cb, 0xf397a467, 0x6998c7f8, 0xfccd650d, 0x6ba3ef15 }, + }, +}; + +TEST_CASE("Test 3/4 Coding Scheme Algorithm", "[bootloader_support]") +{ + const int num_tests = sizeof(coding_scheme_data)/sizeof(coding_scheme_test_t); + for (int i = 0; i < num_tests; i++) { + uint32_t result[8]; + const coding_scheme_test_t *t = &coding_scheme_data[i]; + + printf("Test case %d...\n", i); + esp_err_t r = esp_efuse_apply_34_encoding(t->unencoded, result, sizeof(t->unencoded)); + TEST_ASSERT_EQUAL_HEX(ESP_OK, r); + TEST_ASSERT_EQUAL_HEX32_ARRAY(t->encoded, result, 8); + + // Do the same, 6 bytes at a time + for (int offs = 0; offs < sizeof(t->unencoded); offs += 6) { + bzero(result, sizeof(result)); + r = esp_efuse_apply_34_encoding(t->unencoded + offs, result, 6); + TEST_ASSERT_EQUAL_HEX(ESP_OK, r); + TEST_ASSERT_EQUAL_HEX32_ARRAY(t->encoded + (offs / 6 * 2), result, 2); + } + } +} diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 59b8dd8bfe..9fbe1eec65 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 59b8dd8bfe3927dc11ffc06603fa082cb0f523bb +Subproject commit 9fbe1eec656dc69942f5163cc6cc79c33d5aad64 diff --git a/components/soc/esp32/include/soc/efuse_reg.h b/components/soc/esp32/include/soc/efuse_reg.h index 0be58c4651..6c3f45c542 100644 --- a/components/soc/esp32/include/soc/efuse_reg.h +++ b/components/soc/esp32/include/soc/efuse_reg.h @@ -298,6 +298,9 @@ #define EFUSE_RD_CODING_SCHEME_V 0x3 #define EFUSE_RD_CODING_SCHEME_S 0 +#define EFUSE_CODING_SCHEME_VAL_NONE 0x0 +#define EFUSE_CODING_SCHEME_VAL_34 0x1 + #define EFUSE_BLK0_WDATA0_REG (DR_REG_EFUSE_BASE + 0x01c) /* EFUSE_FLASH_CRYPT_CNT : R/W ;bitpos:[27:20] ;default: 8'b0 ; */ /*description: program for flash_crypt_cnt*/ diff --git a/docs/en/security/flash-encryption.rst b/docs/en/security/flash-encryption.rst index 36a02b8fa3..e9a87913eb 100644 --- a/docs/en/security/flash-encryption.rst +++ b/docs/en/security/flash-encryption.rst @@ -11,7 +11,7 @@ Flash Encryption is separate from the :doc:`Secure Boot ` feature, Background ---------- -- The contents of the flash are encrypted using AES with a 256 bit key. The flash encryption key is stored in efuse internal to the chip, and is (by default) protected from software access. +- The contents of the flash are encrypted using AES-256. The flash encryption key is stored in efuse internal to the chip, and is (by default) protected from software access. - Flash access is transparent via the flash cache mapping feature of ESP32 - any flash regions which are mapped to the address space will be transparently decrypted when read. @@ -29,6 +29,8 @@ Background - The flash encryption key is stored in efuse key block 1, internal to the ESP32 chip. By default, this key is read- and write-protected so software cannot access it or change it. +- By default, the Efuse Block 1 Coding Scheme is "None" and a 256 bit key is stored in this block. On some ESP32s, the Coding Scheme is set to 3/4 Encoding (CODING_SCHEME efuse has value 1) and a 192 bit key must be stored in this block. See ESP32 Technical Reference Manual section 20.3.1.3 *System Parameter coding_scheme* for more details. The algorithm operates on a 256 bit key in all cases, 192 bit keys are extended by repeating some bits (:ref:`details `). The coding scheme is shown in the ``Features`` line when ``esptool.py`` connects to the chip, or in the ``espefuse.py summary`` output. + - The `flash encryption algorithm` is AES-256, where the key is "tweaked" with the offset address of each 32 byte block of flash. This means every 32 byte block (two consecutive 16 byte AES blocks) is encrypted with a unique key derived from the flash encryption key. - Although software running on the chip can transparently decrypt flash contents, by default it is made impossible for the UART bootloader to decrypt (or encrypt) data when flash encryption is enabled. @@ -203,7 +205,7 @@ Flash encryption keys are 32 bytes of random data. You can generate a random key Alternatively, if you're using :doc:`secure boot ` and have a :ref:`secure boot signing key ` then you can generate a deterministic SHA-256 digest of the secure boot private signing key and use this as the flash encryption key:: - espsecure.py digest_private_key --keyfile secure_boot_signing_key.pem my_flash_encryption_key.bin + espsecure.py digest_private_key --keyfile secure_boot_signing_key.pem --keylen 256 my_flash_encryption_key.bin (The same 32 bytes is used as the secure boot digest key if you enable :ref:`reflashable mode` for secure boot.) @@ -402,10 +404,14 @@ Flash Encryption Algorithm - AES-256 operates on 16 byte blocks of data. The flash encryption engine encrypts and decrypts data in 32 byte blocks, two AES blocks in series. -- AES algorithm is used inverted in flash encryption, so the flash encryption "encrypt" operation is AES decrypt and the "decrypt" operation is AES encrypt. This is for performance reasons and does not alter the effectiveness of the algorithm. - - The main flash encryption key is stored in efuse (BLOCK1) and by default is protected from further writes or software readout. +- AES-256 key size is 256 bits (32 bytes), read from efuse block 1. The hardware AES engine uses the key in reversed byte order to the order stored in the efuse block. + - If ``CODING_SCHEME`` efuse is set to 0 (default "None" Coding Scheme) then the efuse key block is 256 bits and the key is stored as-is (in reversed byte order). + - If ``CODING_SCHEME`` efuse is set to 1 (3/4 Encoding) then the efuse key block is 192 bits (in reversed byte order), so overall entropy is reduced. The hardware flash encryption still operates on a 256-bit key, after being read (and un-reversed), the key is extended by as ``key = key[0:255] + key[64:127]``. + +- AES algorithm is used inverted in flash encryption, so the flash encryption "encrypt" operation is AES decrypt and the "decrypt" operation is AES encrypt. This is for performance reasons and does not alter the effectiveness of the algorithm. + - Each 32 byte block (two adjacent 16 byte AES blocks) is encrypted with a unique key. The key is derived from the main flash encryption key in efuse, XORed with the offset of this block in the flash (a "key tweak"). - The specific tweak depends on the setting of ``FLASH_CRYPT_CONFIG`` efuse. This is a 4 bit efuse, where each bit enables XORing of a particular range of the key bits: @@ -419,7 +425,6 @@ Flash Encryption Algorithm - The high 19 bits of the block offset (bit 5 to bit 23) are XORed with the main flash encryption key. This range is chosen for two reasons: the maximum flash size is 16MB (24 bits), and each block is 32 bytes so the least significant 5 bits are always zero. -- There is a particular mapping from each of the 19 block offset bits to the 256 bits of the flash encryption key, to determine which bit is XORed with which. See the variable ``_FLASH_ENCRYPTION_TWEAK_PATTERN`` in the espsecure.py source code for the complete mapping. - -- To see the full flash encryption algorithm implemented in Python, refer to the `_flash_encryption_operation()` function in the espsecure.py source code. +- There is a particular mapping from each of the 19 block offset bits to the 256 bits of the flash encryption key, to determine which bit is XORed with which. See the variable ``_FLASH_ENCRYPTION_TWEAK_PATTERN`` in the ``espsecure.py`` source code for the complete mapping. +- To see the full flash encryption algorithm implemented in Python, refer to the `_flash_encryption_operation()` function in the ``espsecure.py`` source code. diff --git a/docs/en/security/secure-boot.rst b/docs/en/security/secure-boot.rst index 5d5f4a01da..bacff37e1f 100644 --- a/docs/en/security/secure-boot.rst +++ b/docs/en/security/secure-boot.rst @@ -49,6 +49,8 @@ The following keys are used by the secure boot process: - "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from the internal hardware random number generator, the user does not need to supply it (it is optionally possible to supply this key, see :ref:`secure-boot-reflashable`). The Efuse holding this key is read & write protected (preventing software access) before secure boot is enabled. + - By default, the Efuse Block 2 Coding Scheme is "None" and a 256 bit key is stored in this block. On some ESP32s, the Coding Scheme is set to 3/4 Encoding (CODING_SCHEME efuse has value 1) and a 192 bit key must be stored in this block. See ESP32 Technical Reference Manual section 20.3.1.3 *System Parameter coding_scheme* for more details. The algorithm operates on a 256 bit key in all cases, 192 bit keys are extended by repeating some bits (:ref:`details`). + - "secure boot signing key" is a standard ECDSA public/private key pair (see :ref:`secure-boot-image-signing-algorithm`) in PEM format. - The public key from this key pair (for signature verification but not signature creation) is compiled into the software bootloader and used to verify the second stage of booting (partition table, app image) before booting continues. The public key can be freely distributed, it does not need to be kept secret. @@ -113,19 +115,21 @@ Re-Flashable Software Bootloader Configuration "Secure Boot: One-Time Flash" is the recommended configuration for production devices. In this mode, each device gets a unique key that is never stored outside the device. -However, an alternative mode :ref:`Secure Boot: Reflashable ` is also available. This mode allows you to supply a 256-bit key file that is used for the secure bootloader key. As you have the key file, you can generate new bootloader images and secure boot digests for them. +However, an alternative mode :ref:`Secure Boot: Reflashable ` is also available. This mode allows you to supply a binary key file that is used for the secure bootloader key. As you have the key file, you can generate new bootloader images and secure boot digests for them. -In the esp-idf build process, this 256-bit key file is derived from the app signing key generated during the generate_signing_key step above. The private key's SHA-256 digest is used as the 256-bit secure bootloader key. This is a convenience so you only need to generate/protect a single private key. +In the esp-idf build process, this 256-bit key file is derived from the app signing key generated during the generate_signing_key step above. The private key's SHA-256 digest is used as the secure bootloader key (as-is for Coding Scheme None, or truncate to 192 bytes for 3/4 Encoding). This is a convenience so you only need to generate/protect a single private key. .. note:: Although it's possible, we strongly recommend not generating one secure boot key and flashing it to every device in a production environment. The "One-Time Flash" option is recommended for production environments. To enable a reflashable bootloader: -1. In the ``make menuconfig`` step, select "Bootloader Config" -> "Secure Boot" -> "Reflashable". +1. In the ``make menuconfig`` step, select "Bootloader Config" -> :ref:`CONFIG_SECURE_BOOT_ENABLED` -> :ref:`CONFIG_SECURE_BOOTLOADER_MODE` -> Reflashable. + +2. If necessary, set the :ref:`CONFIG_SECURE_BOOTLOADER_KEY_ENCODING` based on the coding scheme used by the device. The coding scheme is shown in the ``Features`` line when ``esptool.py`` connects to the chip, or in the ``espefuse.py summary`` output. 2. Follow the steps shown above to choose a signing key file, and generate the key file. -3. Run ``make bootloader``. A 256-bit key file will be created, derived from the private key that is used for signing. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the bootloader key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-calculated digest (generated during the build process). +3. Run ``make bootloader``. A binary key file will be created, derived from the private key that is used for signing. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the bootloader key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-calculated digest (generated during the build process). 4. Resume from :ref:`Step 6 of the one-time flashing process `, to flash the bootloader and enable secure boot. Watch the console log output closely to ensure there were no errors in the secure boot configuration. @@ -198,6 +202,8 @@ The first stage of secure boot verification (checking the software bootloader) i 3. Generate a digest from data (usually the bootloader image from flash) using the same algorithm as step 2 and compare it to a pre-calculated digest supplied in a buffer (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. This function is available even when Efuse ABS_DONE_0 is burned. +.. _secure-bootloader-digest-algorithm: + Secure Bootloader Digest Algorithm ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -207,15 +213,16 @@ For a Python version of this algorithm, see the ``espsecure.py`` tool in the com Items marked with (^) are to fulfill hardware restrictions, as opposed to cryptographic restrictions. -1. Prefix the image with a 128 byte randomly generated IV. -2. If the image length is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) -3. For each 16 byte plaintext block of the input image: +1. Read the AES key from efuse block 2, in reversed byte order. If Coding Scheme is set to 3/4 Encoding, extend the 192 bit key to 256 bits using the same algorithm described in :ref:`flash-encryption-algorithm`. +2. Prefix the image with a 128 byte randomly generated IV. +3. If the image length is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) +4. For each 16 byte plaintext block of the input image: - Reverse the byte order of the plaintext input block (^) - Apply AES256 in ECB mode to the plaintext block. - Reverse the byte order of the ciphertext output block. (^) - Append to the overall ciphertext output. -4. Byte-swap each 4 byte word of the ciphertext (^) -5. Calculate SHA-512 of the ciphertext. +5. Byte-swap each 4 byte word of the ciphertext (^) +6. Calculate SHA-512 of the ciphertext. Output digest is 192 bytes of data: The 128 byte IV, followed by the 64 byte SHA-512 digest.