diff --git a/config/esp32/partitions.csv b/config/esp32/partitions.csv index 89fe79f..3becf08 100755 --- a/config/esp32/partitions.csv +++ b/config/esp32/partitions.csv @@ -1,7 +1,7 @@ # Name, Type, SubType, Offset, Size, Flags # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap -nvs, data, nvs, 0x9000, 0x6000 -phy_init, data, phy, 0xf000, 0x1000 -factory, app, factory, 0x10000, 1M, +nvs, data, nvs, 0x11000, 0x6000 +phy_init, data, phy, 0x17000, 0x1000 +factory, app, factory, 0x20000, 1M part0, 0x40, 0x1, 0x200000, 1M, diff --git a/src/fs/otp.c b/src/fs/otp.c index f31704d..a89107c 100644 --- a/src/fs/otp.c +++ b/src/fs/otp.c @@ -91,6 +91,137 @@ static void otp_lock_page(uint8_t page) { uint8_t _otp_key_1[32] = {0}; uint8_t _otp_key_2[32] = {0}; +static const uint8_t esp_secure_boot_digest[32] = { + 0x0c, 0x1e, 0xce, 0xf3, 0xb4, 0x8f, 0x4a, 0x81, + 0x45, 0x6c, 0x85, 0x39, 0x15, 0xcc, 0x05, 0x36, + 0xbe, 0x23, 0x24, 0xee, 0xac, 0x8e, 0x3b, 0xb5, + 0x77, 0x6f, 0x2d, 0xb9, 0x62, 0x38, 0x75, 0x6a +}; + +static esp_efuse_purpose_t esp_secure_boot_purpose(uint8_t digest_idx) { + switch (digest_idx) { + case 0: return ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST0; + case 1: return ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST1; + case 2: return ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST2; + default: return ESP_EFUSE_KEY_PURPOSE_MAX; + } +} + +static bool esp_find_secure_boot_block(uint8_t digest_idx, esp_efuse_block_t *out_block) { + esp_efuse_purpose_t purpose = esp_secure_boot_purpose(digest_idx); + if (purpose == ESP_EFUSE_KEY_PURPOSE_MAX) { + return false; + } + for (esp_efuse_block_t blk = EFUSE_BLK_KEY0; blk < EFUSE_BLK_KEY_MAX; blk++) { + if (esp_efuse_get_key_purpose(blk) == purpose) { + if (out_block) { + *out_block = blk; + } + return true; + } + } + return false; +} + +static esp_err_t esp_provision_secure_boot_digest(uint8_t digest_idx, esp_efuse_block_t *out_block) { + esp_efuse_purpose_t purpose = esp_secure_boot_purpose(digest_idx); + if (purpose == ESP_EFUSE_KEY_PURPOSE_MAX) { + return ESP_ERR_INVALID_ARG; + } + + esp_efuse_block_t block = EFUSE_BLK_KEY_MAX; + if (esp_find_secure_boot_block(digest_idx, &block)) { + const esp_efuse_desc_t **key_desc = esp_efuse_get_key(block); + if (!key_desc) { + return ESP_FAIL; + } + uint8_t existing[32] = {0}; + esp_err_t err = esp_efuse_read_field_blob(key_desc, existing, sizeof(existing) * 8); + if (err != ESP_OK) { + return err; + } + if (memcmp(existing, esp_secure_boot_digest, sizeof(existing)) != 0) { + return ESP_ERR_INVALID_STATE; + } + if (out_block) { + *out_block = block; + } + return ESP_OK; + } + + block = esp_efuse_find_unused_key_block(); + if (block == EFUSE_BLK_KEY_MAX) { + return ESP_ERR_NOT_FOUND; + } + + esp_err_t err = esp_efuse_batch_write_begin(); + if (err != ESP_OK) { + return err; + } + + err = esp_efuse_set_key_purpose(block, purpose); + if (err == ESP_OK) { + const esp_efuse_desc_t **key_desc = esp_efuse_get_key(block); + if (!key_desc) { + err = ESP_FAIL; + } else { + err = esp_efuse_write_field_blob(key_desc, esp_secure_boot_digest, sizeof(esp_secure_boot_digest) * 8); + } + } + + if (err == ESP_OK) { + err = esp_efuse_batch_write_commit(); + } else { + esp_efuse_batch_write_cancel(); + } + + if (err == ESP_OK && out_block) { + *out_block = block; + } + return err; +} + +static esp_err_t esp_disable_debug_interfaces(void) { + esp_err_t err = ESP_OK; + +#ifdef ESP_EFUSE_SOFT_DIS_JTAG + err = esp_efuse_write_field_bit(ESP_EFUSE_SOFT_DIS_JTAG); + if (err != ESP_OK) { + return err; + } +#endif + +#ifdef ESP_EFUSE_HARD_DIS_JTAG + err = esp_efuse_write_field_bit(ESP_EFUSE_HARD_DIS_JTAG); + if (err != ESP_OK) { + return err; + } +#endif + +#ifdef ESP_EFUSE_DIS_USB_JTAG + err = esp_efuse_write_field_bit(ESP_EFUSE_DIS_USB_JTAG); + if (err != ESP_OK) { + return err; + } +#endif + +#ifdef ESP_EFUSE_DIS_USB_SERIAL_JTAG + err = esp_efuse_write_field_bit(ESP_EFUSE_DIS_USB_SERIAL_JTAG); + if (err != ESP_OK) { + return err; + } +#endif + +#ifdef ESP_EFUSE_DIS_PAD_JTAG + err = esp_efuse_write_field_bit(ESP_EFUSE_DIS_PAD_JTAG); + if (err != ESP_OK) { + return err; + } +#endif + + return err; +} + esp_err_t read_key_from_efuse(esp_efuse_block_t block, uint8_t *key, size_t key_len) { const esp_efuse_desc_t **key_desc = esp_efuse_get_key(block); @@ -124,6 +255,9 @@ typedef esp_err_t otp_ret_t; #ifndef SECURE_BOOT_BOOTKEY_INDEX #define SECURE_BOOT_BOOTKEY_INDEX 0 #endif +#ifndef PICO_KEYS_REQUIRE_SECURE_BOOT_BEFORE_LOCK +#define PICO_KEYS_REQUIRE_SECURE_BOOT_BEFORE_LOCK 1 +#endif bool otp_is_secure_boot_enabled(uint8_t *bootkey) { #ifdef PICO_RP2350 @@ -151,7 +285,27 @@ bool otp_is_secure_boot_enabled(uint8_t *bootkey) { } return true; #elif defined(ESP_PLATFORM) - // TODO: Implement secure boot check for ESP32-S3 + if (!esp_efuse_read_field_bit(ESP_EFUSE_SECURE_BOOT_EN)) { + return false; + } + + uint8_t preferred = SECURE_BOOT_BOOTKEY_INDEX; + if (preferred <= 2 && esp_find_secure_boot_block(preferred, NULL) + && !esp_efuse_get_digest_revoke(preferred)) { + if (bootkey) { + *bootkey = preferred; + } + return true; + } + + for (uint8_t idx = 0; idx <= 2; idx++) { + if (esp_find_secure_boot_block(idx, NULL) && !esp_efuse_get_digest_revoke(idx)) { + if (bootkey) { + *bootkey = idx; + } + return true; + } + } #endif return false; } @@ -174,7 +328,15 @@ bool otp_is_secure_boot_locked() { } return bootkey_idx != 0xFF; #elif defined(ESP_PLATFORM) - // TODO: Implement secure boot lock check for ESP32-S3 + for (uint8_t idx = 0; idx <= 2; idx++) { + if (idx == bootkey_idx) { + continue; + } + if (!esp_efuse_get_digest_revoke(idx)) { + return false; + } + } + return true; #endif return false; } @@ -224,7 +386,69 @@ int otp_enable_secure_boot(uint8_t bootkey, bool secure_lock) { PICOKEY_CHECK(otp_write_data_raw(OTP_DATA_PAGE2_LOCK1_ROW, flagsp2, sizeof(flagsp2))); } #elif defined(ESP_PLATFORM) - // TODO: Implement secure boot for ESP32-S3 + if (bootkey > 2) { + return ESP_ERR_INVALID_ARG; + } + + if (secure_lock && PICO_KEYS_REQUIRE_SECURE_BOOT_BEFORE_LOCK) { + if (!esp_efuse_read_field_bit(ESP_EFUSE_SECURE_BOOT_EN)) { + printf("Secure lock requires SECURE_BOOT_EN already set. Enable secure boot first.\n"); + return ESP_ERR_INVALID_STATE; + } + } + + esp_efuse_block_t key_block = EFUSE_BLK_KEY_MAX; + esp_err_t err = esp_provision_secure_boot_digest(bootkey, &key_block); + if (err != ESP_OK) { + printf("Error provisioning secure boot digest %u [%d]\n", bootkey, err); + return err; + } + + if (!esp_efuse_read_field_bit(ESP_EFUSE_SECURE_BOOT_EN)) { + err = esp_efuse_write_field_bit(ESP_EFUSE_SECURE_BOOT_EN); + if (err != ESP_OK) { + printf("Error enabling secure boot [%d]\n", err); + return err; + } + } + + if (secure_lock) { + for (uint8_t idx = 0; idx <= 2; idx++) { + if (idx == bootkey) { + continue; + } + err = esp_efuse_set_digest_revoke(idx); + if (err != ESP_OK) { + printf("Error revoking secure boot digest %u [%d]\n", idx, err); + return err; + } + } + + err = esp_efuse_set_key_dis_write(key_block); + if (err != ESP_OK) { + printf("Error setting secure boot key block read only [%d]\n", err); + return err; + } + err = esp_efuse_set_keypurpose_dis_write(key_block); + if (err != ESP_OK) { + printf("Error setting secure boot key purpose read only [%d]\n", err); + return err; + } + + /* // Not sure if it allows future upgrades if ROM download mode is disabled, so leaving it enabled for now + err = esp_efuse_disable_rom_download_mode(); + if (err != ESP_OK) { + printf("Error disabling ROM download mode [%d]\n", err); + return err; + } + */ + + err = esp_disable_debug_interfaces(); + if (err != ESP_OK) { + printf("Error disabling JTAG interfaces [%d]\n", err); + return err; + } + } #else (void)bootkey; (void)secure_lock; diff --git a/src/fs/otp.h b/src/fs/otp.h index 71aaabf..3ff7603 100644 --- a/src/fs/otp.h +++ b/src/fs/otp.h @@ -38,6 +38,7 @@ extern int otp_write_data_raw(uint16_t row, const uint8_t *data, uint16_t len); #elif defined(ESP_PLATFORM) #include "esp_efuse.h" +#include "esp_efuse_table.h" #define OTP_KEY_1 EFUSE_BLK_KEY3 #define OTP_KEY_2 EFUSE_BLK_KEY4