Merge branch 'bugfix/secure_boot_regression_partitions' into 'master'

Fix secure boot & flash encryption regression, don't verify partitions as part of secure boot

See merge request idf/esp-idf!2764
This commit is contained in:
Angus Gratton
2018-07-16 12:07:56 +08:00
10 changed files with 88 additions and 57 deletions

View File

@ -107,7 +107,7 @@ bootloader: $(BOOTLOADER_DIGEST_BIN)
$(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY)
@echo "DIGEST $(notdir $@)"
$(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $<
$(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $<
else # CONFIG_SECURE_BOOT_ENABLED && !CONFIG_SECURE_BOOTLOADER_REFLASHABLE && !CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH
bootloader:
@ -116,7 +116,7 @@ bootloader:
endif
ifndef CONFIG_SECURE_BOOT_ENABLED
# don't build bootloader by default is secure boot is enabled
# don't build bootloader by default if secure boot is enabled
all_binaries: $(BOOTLOADER_BIN)
endif

View File

@ -41,19 +41,25 @@ SECTIONS
*(.iram1 .iram1.*) /* catch stray IRAM_ATTR */
*liblog.a:(.literal .text .literal.* .text.*)
*libgcc.a:(.literal .text .literal.* .text.*)
*libbootloader_support.a:bootloader_utility.o(.literal .text .literal.* .text.*)
*libbootloader_support.a:esp_image_format.o(.literal .text .literal.* .text.*)
*libbootloader_support.a:bootloader_random.o(.literal .text .literal.* .text.*)
*libbootloader_support.a:bootloader_flash.o(.literal .text .literal.* .text.*)
*libbootloader_support.a:flash_partitions.o(.literal .text .literal.* .text.*)
*libbootloader_support.a:bootloader_random.o(.literal .text .literal.* .text.*)
*libbootloader_support.a:bootloader_utility.o(.literal .text .literal.* .text.*)
*libbootloader_support.a:bootloader_sha.o(.literal .text .literal.* .text.*)
*libbootloader_support.a:efuse.o(.literal .text .literal.* .text.*)
*libbootloader_support.a:esp_image_format.o(.literal .text .literal.* .text.*)
*libbootloader_support.a:flash_encrypt.o(.literal .text .literal.* .text.*)
*libbootloader_support.a:flash_partitions.o(.literal .text .literal.* .text.*)
*libbootloader_support.a:secure_boot.o(.literal .text .literal.* .text.*)
*libbootloader_support.a:secure_boot_signatures.o(.literal .text .literal.* .text.*)
*libmicro-ecc.a:*.o(.literal .text .literal.* .text.*)
*libspi_flash.a:*.o(.literal .text .literal.* .text.*)
*(.fini.literal)
*(.fini)
*(.gnu.version)
_text_end = ABSOLUTE(.);
_etext = .;
} > iram_loader_seg
.iram.text :
{
. = ALIGN (16);

View File

@ -27,7 +27,7 @@
#define ESP_PARTITION_TABLE_MAX_LEN 0xC00 /* Maximum length of partition table data */
#define ESP_PARTITION_TABLE_MAX_ENTRIES (ESP_PARTITION_TABLE_MAX_LEN / sizeof(esp_partition_info_t)) /* Maximum length of partition table data, including terminating entry */
/* @brief Verify the partition table (does not include verifying secure boot cryptographic signature)
/* @brief Verify the partition table
*
* @param partition_table Pointer to at least ESP_PARTITION_TABLE_MAX_ENTRIES of potential partition table data. (ESP_PARTITION_TABLE_MAX_LEN bytes.)
* @param log_errors Log errors if the partition table is invalid.
@ -35,6 +35,13 @@
*
* @return ESP_OK on success, ESP_ERR_INVALID_STATE if partition table is not valid.
*/
esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions);
esp_err_t esp_partition_table_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions);
/* This function is included for compatibility with the ESP-IDF v3.x API */
inline static __attribute__((deprecated)) esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions)
{
return esp_partition_table_verify(partition_table, log_errors, num_partitions);
}
#endif

View File

@ -100,18 +100,6 @@ bool bootloader_common_erase_part_type_data(const char *list_erase, bool ota_dat
int num_partitions;
bool ret = true;
#ifdef CONFIG_SECURE_BOOT_ENABLED
if (esp_secure_boot_enabled()) {
ESP_LOGI(TAG, "Verifying partition table signature...");
err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_MAX_LEN);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to verify partition table signature.");
return false;
}
ESP_LOGD(TAG, "Partition table signature verified");
}
#endif
partitions = bootloader_mmap(ESP_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_MAX_LEN);
if (!partitions) {
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", ESP_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_MAX_LEN);
@ -119,7 +107,7 @@ bool bootloader_common_erase_part_type_data(const char *list_erase, bool ota_dat
}
ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", ESP_PARTITION_TABLE_OFFSET, (intptr_t)partitions);
err = esp_partition_table_basic_verify(partitions, true, &num_partitions);
err = esp_partition_table_verify(partitions, true, &num_partitions);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to verify partition table");
ret = false;

View File

@ -72,18 +72,6 @@ bool bootloader_utility_load_partition_table(bootloader_state_t* bs)
esp_err_t err;
int num_partitions;
#ifdef CONFIG_SECURE_BOOT_ENABLED
if(esp_secure_boot_enabled()) {
ESP_LOGI(TAG, "Verifying partition table signature...");
err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_MAX_LEN);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to verify partition table signature.");
return false;
}
ESP_LOGD(TAG, "Partition table signature verified");
}
#endif
partitions = bootloader_mmap(ESP_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_MAX_LEN);
if (!partitions) {
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", ESP_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_MAX_LEN);
@ -91,7 +79,7 @@ bool bootloader_utility_load_partition_table(bootloader_state_t* bs)
}
ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", ESP_PARTITION_TABLE_OFFSET, (intptr_t)partitions);
err = esp_partition_table_basic_verify(partitions, true, &num_partitions);
err = esp_partition_table_verify(partitions, true, &num_partitions);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to verify partition table");
return false;

View File

@ -254,7 +254,7 @@ static esp_err_t encrypt_and_load_partition_table(esp_partition_info_t *partitio
ESP_LOGE(TAG, "Failed to read partition table data");
return err;
}
if (esp_partition_table_basic_verify(partition_table, false, num_partitions) == ESP_OK) {
if (esp_partition_table_verify(partition_table, false, num_partitions) == ESP_OK) {
ESP_LOGD(TAG, "partition table is plaintext. Encrypting...");
esp_err_t err = esp_flash_encrypt_region(ESP_PARTITION_TABLE_OFFSET,
FLASH_SECTOR_SIZE);

View File

@ -20,7 +20,7 @@
static const char *TAG = "flash_parts";
esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions)
esp_err_t esp_partition_table_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions)
{
int md5_found = 0;
int num_parts;

View File

@ -43,6 +43,10 @@ config PARTITION_TABLE_OFFSET
Allows you to move the partition table, it gives more space for the bootloader.
Note that the bootloader and app will both need to be compiled with the same PARTITION_TABLE_OFFSET value.
This number should be a multiple of 0x1000.
Note that partition offsets in the partition table CSV file may need to be changed if this value is set to a higher value.
config PARTITION_TABLE_MD5
bool "Generate an MD5 checksum for the partition table"
default y

View File

@ -5,7 +5,8 @@ Flash Encryption is a feature for encrypting the contents of the ESP32's attache
Flash Encryption is separate from the :doc:`Secure Boot <secure-boot>` feature, and you can use flash encryption without enabling secure boot. However we recommend using both features together for a secure environment.
**IMPORTANT: Enabling flash encryption limits your options for further updates of your ESP32. Make sure to read this document (including :ref:`flash-encryption-limitations`) and understand the implications of enabling flash encryption.**
.. important::
Enabling flash encryption limits your options for further updates of your ESP32. Make sure to read this document (including :ref:`flash-encryption-limitations`) and understand the implications of enabling flash encryption.
Background
----------
@ -41,9 +42,10 @@ Flash Encryption Initialisation
This is the default (and recommended) flash encryption initialisation process. It is possible to customise this process for development or other purposes, see :ref:`flash-encryption-advanced-features` for details.
**IMPORTANT: Once flash encryption is enabled on first boot, the hardware allows a maximum of 3 subsequent flash updates via serial re-flashing.** A special procedure (documented in :ref:`updating-encrypted-flash-serial`) must be followed to perform these updates.
.. important::
Once flash encryption is enabled on first boot, the hardware allows a maximum of 3 subsequent flash updates via serial re-flashing. A special procedure (documented in :ref:`updating-encrypted-flash-serial`) must be followed to perform these updates.
- If secure boot is enabled, no physical re-flashes are possible.
- If secure boot is enabled, physical reflashing with plaintext data requires a "Reflashable" secure boot digest (see :ref:`flash-encryption-and-secure-boot`).
- OTA updates can be used to update flash content without counting towards this limit.
- When enabling flash encryption in development, use a `pregenerated flash encryption key` to allow physically re-flashing an unlimited number of times with pre-encrypted data.**
@ -55,11 +57,14 @@ Process to enable flash encryption:
- Build and flash the bootloader, partition table and factory app image as normal. These partitions are initially written to the flash unencrypted.
.. note:: The bootloader app binary ``bootloader.bin`` may become too large when both secure boot and flash encryption are enabled. See :ref:`secure-boot-bootloader-size`.
- On first boot, the bootloader sees :ref:`FLASH_CRYPT_CNT` is set to 0 (factory default) so it generates a flash encryption key using the hardware random number generator. This key is stored in efuse. The key is read and write protected against further software access.
- All of the encrypted partitions are then encrypted in-place by the bootloader. Encrypting in-place can take some time (up to a minute for large partitions.)
**IMPORTANT: Do not interrupt power to the ESP32 while the first boot encryption pass is running. If power is interrupted, the flash contents will be corrupted and require flashing with unencrypted data again. A reflash like this will not count towards the flashing limit.**
.. important::
Do not interrupt power to the ESP32 while the first boot encryption pass is running. If power is interrupted, the flash contents will be corrupted and require flashing with unencrypted data again. A reflash like this will not count towards the flashing limit.
- Once flashing is complete. efuses are blown (by default) to disable encrypted flash access while the UART bootloader is running. See :ref:`uart-bootloader-encryption` for advanced details.
@ -88,7 +93,8 @@ Whenever the :ref:`FLASH_CRYPT_CNT` is set to a value with an odd number of bits
- Any data accessed via :func:`esp_spi_flash_mmap`.
- The software bootloader image when it is read by the ROM bootloader.
**IMPORTANT: The MMU flash cache unconditionally decrypts all data. Data which is stored unencrypted in the flash will be "transparently decrypted" via the flash cache and appear to software like random garbage.**
.. important::
The MMU flash cache unconditionally decrypts all data. Data which is stored unencrypted in the flash will be "transparently decrypted" via the flash cache and appear to software like random garbage.
Reading Encrypted Flash
^^^^^^^^^^^^^^^^^^^^^^^
@ -129,14 +135,14 @@ OTA updates to encrypted partitions will automatically write encrypted, as long
Serial Flashing
^^^^^^^^^^^^^^^
Provided secure boot is not used, the :ref:`FLASH_CRYPT_CNT` allows the flash to be updated with new plaintext data via serial flashing (or other physical methods), up to 3 additional times.
The :ref:`FLASH_CRYPT_CNT` allows the flash to be updated with new plaintext data via serial flashing (or other physical methods), up to 3 additional times.
The process involves flashing plaintext data, and then bumping the value of :ref:`FLASH_CRYPT_CNT` which causes the bootloader to re-encrypt this data.
Limited Updates
~~~~~~~~~~~~~~~
Only 4 serial flash update cycles of this kind are possible, including the initial encrypted flash.
Only 4 plaintext serial update cycles of this kind are possible, including the initial encrypted flash.
After the fourth time encryption is disabled, :ref:`FLASH_CRYPT_CNT` has the maximum value `0xFF` and encryption is permanently disabled.
@ -149,7 +155,7 @@ Cautions With Serial Flashing
- Using ``make flash`` should flash all partitions which need to be flashed.
- If secure boot is enabled, you can't reflash via serial at all unless you used the "Reflashable" option for Secure Boot, pre-generated a key and burned it to the ESP32 (refer to :doc:`Secure Boot <secure-boot>` docs.). In this case you can re-flash a plaintext secure boot digest and bootloader image at offset 0x0. It is necessary to re-flash this digest before flashing other plaintext data.
- If secure boot is enabled, you can't reflash plaintext data via serial at all unless you used the "Reflashable" option for Secure Boot. See :ref:`flash-encryption-and-secure-boot`.
Serial Re-Flashing Procedure
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -181,9 +187,10 @@ Reflashing via Pregenerated Flash Encryption Key
It is possible to pregenerate a flash encryption key on the host computer and burn it into the ESP32's efuse key block. This allows data to be pre-encrypted on the host and flashed to the ESP32 without needing a plaintext flash update.
This is useful for development, because it removes the 4 time reflashing limit. It also allows reflashing with secure boot enabled, because the bootloader doesn't need to be reflashed each time.
This is useful for development, because it removes the 4 time reflashing limit. It also allows reflashing the app with secure boot enabled, because the bootloader doesn't need to be reflashed each time.
**IMPORTANT This method is intended to assist with development only, not for production devices. If pre-generating flash encryption for production, ensure the keys are generated from a high quality random number source and do not share the same flash encryption key across multiple devices.**
.. important::
This method is intended to assist with development only, not for production devices. If pre-generating flash encryption for production, ensure the keys are generated from a high quality random number source and do not share the same flash encryption key across multiple devices.
Pregenerating a Flash Encryption Key
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -194,7 +201,7 @@ Flash encryption keys are 32 bytes of random data. You can generate a random key
(The randomness of this data is only as good as the OS and it's Python installation's random data source.)
Alternatively, if you're using :doc:`secure boot <secure-boot>` and have a 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::
Alternatively, if you're using :doc:`secure boot <secure-boot>` and have a :ref:`secure boot signing key <secure-boot-generate-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
@ -268,7 +275,18 @@ Flash Encryption prevents plaintext readout of the encrypted flash, to protect f
- For the same reason, an attacker can always tell when a pair of adjacent 16 byte blocks (32 byte aligned) contain identical content. Keep this in mind if storing sensitive data on the flash, design your flash storage so this doesn't happen (using a counter byte or some other non-identical value every 16 bytes is sufficient).
- Flash encryption alone may not prevent an attacker from modifying the firmware of the device. To prevent unauthorised firmware from runningon the device, use flash encryption in combination with :doc:`Secure Boot <secure-boot>`.
- Flash encryption alone may not prevent an attacker from modifying the firmware of the device. To prevent unauthorised firmware from running on the device, use flash encryption in combination with :doc:`Secure Boot <secure-boot>`.
.. _flash-encryption-and-secure-boot:
Flash Encryption & Secure Boot
------------------------------
It is recommended to use flash encryption and secure boot together. However, if Secure Boot is enabled then additional restrictions apply to reflashing the device:
- :ref:`updating-encrypted-flash-ota` are not restricted (provided the new app is signed correctly with the Secure Boot signing key).
- :ref:`Plaintext serial flash updates <updating-encrypted-flash-serial>` are only possible if the :envvar:`Reflashable <CONFIG_SECURE_BOOTLOADER_REFLASHABLE>` Secure Boot mode is selected and a Secure Boot key was pre-generated and burned to the ESP32 (refer to :ref:`Secure Boot <secure-boot-reflashable>` docs.). In this configuration, ``make bootloader`` will produce a pre-digested bootloader and secure boot digest file for flashing at offset 0x0. When following the plaintext serial reflashing steps it is necessary to re-flash this file before flashing other plaintext data.
- :ref:`pregenerated-flash-encryption-key` is still possible, provided the bootloader is not reflashed. Reflashing the bootloader requires the same :envvar:`Reflashable <CONFIG_SECURE_BOOTLOADER_REFLASHABLE>` option to be enabled in the Secure Boot config.
.. _flash-encryption-advanced-features:
@ -320,9 +338,11 @@ It is possible to burn only some of these efuses, and write-protect the rest (wi
(Note that all 3 of these efuses are disabled via one write protect bit, so write protecting one will write protect all of them. For this reason, it's necessary to set any bits before write-protecting.)
**IMPORTANT**: Write protecting these efuses to keep them unset is not currently very useful, as ``esptool.py`` does not support writing or reading encrypted flash.
.. important::
Write protecting these efuses to keep them unset is not currently very useful, as ``esptool.py`` does not support writing or reading encrypted flash.
**IMPORTANT**: If ``DISABLE_DL_DECRYPT`` is left unset (0) this effectively makes flash encryption useless, as an attacker with physical access can use UART bootloader mode (with custom stub code) to read out the flash contents.
.. important::
If ``DISABLE_DL_DECRYPT`` is left unset (0) this effectively makes flash encryption useless, as an attacker with physical access can use UART bootloader mode (with custom stub code) to read out the flash contents.
.. _setting-flash-crypt-config:

View File

@ -55,6 +55,20 @@ The following keys are used by the secure boot process:
- The private key from this key pair *must be securely kept private*, as anyone who has this key can authenticate to any bootloader that is configured with secure boot and the matching public key.
.. _secure-boot-bootloader-size:
Bootloader Size
---------------
When secure boot is enabled the bootloader app binary ``bootloader.bin`` may exceed the default bootloader size limit. This is especially likely if flash encryption is enabled as well. The default size limit is 0x7000 (28672) bytes (partition table offset 0x8000 - bootloader offset 0x1000).
If the bootloader becomes too large, the ESP32 will fail to boot - errors will be logged about either invalid partition table or invalid bootloader checksum.
Options to work around this are:
- Reduce :envvar:`bootloader log level <CONFIG_LOG_BOOTLOADER_LEVEL>`. Setting log level to Warning, Error or None all significantly reduce the final binary size (but may make it harder to debug).
- Set :envvar:`partition table offset <CONFIG_PARTITION_TABLE_OFFSET>` to a higher value than 0x8000, to place the partition table later in the flash. This increases the space available for the bootloader. If the :doc:`partition table </api-guides/partition-tables>` CSV file contains explicit partition offsets, they will need changing so no partition has an offset lower than ``CONFIG_PARTITION_TABLE_OFFSET + 0x1000``. (This includes the default partition CSV files supplied with ESP-IDF.)
.. _secure-boot-howto:
How To Enable Secure Boot
@ -68,9 +82,11 @@ How To Enable Secure Boot
4. The first time you run ``make``, if the signing key is not found then an error message will be printed with a command to generate a signing key via ``espsecure.py generate_signing_key``.
**IMPORTANT** A signing key generated this way will use the best random number source available to the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the private key will be weak.
.. important::
A signing key generated this way will use the best random number source available to the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the private key will be weak.
**IMPORTANT** For production environments, we recommend generating the keypair using openssl or another industry standard encryption program. See :ref:`secure-boot-generate-key` for more details.
.. important::
For production environments, we recommend generating the keypair using openssl or another industry standard encryption program. See :ref:`secure-boot-generate-key` for more details.
5. Run ``make bootloader`` to build a secure boot enabled bootloader. The output of ``make`` will include a prompt for a flashing command, using ``esptool.py write_flash``.
@ -80,11 +96,13 @@ How To Enable Secure Boot
7. Run ``make flash`` to build and flash the partition table and the just-built app image. The app image will be signed using the signing key you generated in step 4.
*NOTE*: ``make flash`` doesn't flash the bootloader if secure boot is enabled.
.. note:: ``make flash`` doesn't flash the bootloader if secure boot is enabled.
8. Reset the ESP32 and it will boot the software bootloader you flashed. The software bootloader will enable secure boot on the chip, and then it verifies the app image signature and boots the app. You should watch the serial console output from the ESP32 to verify that secure boot is enabled and no errors have occurred due to the build configuration.
**NOTE** Secure boot won't be enabled until after a valid partition table and app image have been flashed. This is to prevent accidents before the system is fully configured.
.. note:: Secure boot won't be enabled until after a valid partition table and app image have been flashed. This is to prevent accidents before the system is fully configured.
.. note:: If the ESP32 is reset or powered down during the first boot, it will start the process again on the next boot.
9. On subsequent boots, the secure boot hardware will verify the software bootloader has not changed (using the secure bootloader key) and then the software bootloader will verify the signed partition table and app image (using the public key portion of the secure boot signing key).
@ -95,11 +113,11 @@ 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 "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 :envvar:`Secure Boot: Reflashable <CONFIG_SECURE_BOOTLOADER_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.
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.
**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.
.. 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: