diff --git a/docs/en/security/flash-encryption.rst b/docs/en/security/flash-encryption.rst index e92c824c10..5b64fe7612 100644 --- a/docs/en/security/flash-encryption.rst +++ b/docs/en/security/flash-encryption.rst @@ -193,6 +193,7 @@ During the development stage, there is a frequent need to program different plai Hence, two different flash encryption configurations were created: for development and for production. For details on these configurations, see Section `Flash Encryption Configuration`_. + Flash Encryption Configuration ------------------------------ @@ -201,10 +202,8 @@ The following flash encryption modes are available: - :ref:`flash-enc-development-mode` - recommended for use ONLY DURING DEVELOPMENT, as it does not prevent modification and readout of encrypted flash contents. - :ref:`flash-enc-release-mode` - recommended for manufacturing and production to prevent physical readout of encrypted flash contents. - This section provides information on the mentioned flash encryption modes and step by step instructions on how to use them. - .. _flash-enc-development-mode: Development Mode @@ -230,8 +229,8 @@ To test flash encryption process, take the following steps: - :ref:`Enable flash encryption on boot ` - :ref:`Select encryption mode ` (**Development mode** by default) - :esp32: - :ref: `Select UART ROM download mode ` (**enabled** by default. Note that for the esp32 target, the choice is only available when :ref:`CONFIG_ESP32_REV_MIN` level is set to 3 (ESP32 V3)). - :not esp32: - :ref: `Select UART ROM download mode ` (**enabled** by default.) + :esp32: - :ref:`Select UART ROM download mode ` (**enabled** by default. Note that for the esp32 target, the choice is only available when :ref:`CONFIG_ESP32_REV_MIN` level is set to 3 (ESP32 V3)). + :not esp32: - :ref:`Select UART ROM download mode ` (**enabled** by default.) :esp32s2 or esp32s3: - Set :ref:`Size of generated AES-XTS key ` - :ref:`Select the appropriate bootloader log verbosity ` - Save the configuration and exit. @@ -256,7 +255,6 @@ A sample output of the first {IDF_TARGET_NAME} boot after enabling flash encrypt :start-after: first_boot_enc :end-before: ------ - A sample output of subsequent {IDF_TARGET_NAME} boots just mentions that flash encryption is already enabled: .. include:: {IDF_TARGET_PATH_NAME}_log.inc @@ -311,7 +309,6 @@ To use a host generated key, take the following steps: espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin - 3. **Before the first encrypted boot**, burn the key into your device's eFuse using the command below. This action can be done **only once**. .. only:: not SOC_FLASH_ENCRYPTION_XTS_AES @@ -326,15 +323,15 @@ To use a host generated key, take the following steps: espefuse.py --port PORT burn_key BLOCK my_flash_encryption_key.bin KEYPURPOSE - where `BLOCK` is a free keyblock between `BLOCK_KEY0` and `BLOCK_KEY5`. And `KEYPURPOSE` is either `AES_256_KEY_1`, `XTS_AES_256_KEY_2`, `XTS_AES_128_KEY`. See `{IDF_TARGET_NAME} Technical Reference Manual <{IDF_TARGET_TRM_EN_URL}>`_ for a description of the key purposes. + where ``BLOCK`` is a free keyblock between ``BLOCK_KEY0`` and ``BLOCK_KEY5``. And ``KEYPURPOSE`` is either ``AES_256_KEY_1``, ``XTS_AES_256_KEY_2``, ``XTS_AES_128_KEY``. See `{IDF_TARGET_NAME} Technical Reference Manual <{IDF_TARGET_TRM_EN_URL}>`_ for a description of the key purposes. - AES-128 (256-bit key) - `XTS_AES_128_KEY`: + AES-128 (256-bit key) - ``XTS_AES_128_KEY``: .. code-block:: bash espefuse.py --port PORT burn_key BLOCK my_flash_encryption_key.bin XTS_AES_128_KEY - AES-256 (512-bit key) - `XTS_AES_256_KEY_1` and `XTS_AES_256_KEY_2`. It is not fully supported yet in espefuse.py and espsecure.py. Need to do the following steps: + AES-256 (512-bit key) - ``XTS_AES_256_KEY_1`` and ``XTS_AES_256_KEY_2``. It is not fully supported yet in espefuse.py and espsecure.py. Need to do the following steps: .. code-block:: bash @@ -348,7 +345,7 @@ To use a host generated key, take the following steps: espefuse.py --port PORT burn_key BLOCK my_flash_encryption_key.bin XTS_AES_128_KEY - where `BLOCK` is a free keyblock between `BLOCK_KEY0` and `BLOCK_KEY5`. + where ``BLOCK`` is a free keyblock between ``BLOCK_KEY0`` and ``BLOCK_KEY5``. If the key is not burned and the device is started after enabling flash encryption, the {IDF_TARGET_NAME} will generate a random key that software cannot access or modify. @@ -706,9 +703,9 @@ Key Points About Flash Encryption :esp32: - 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 that every 32-byte block (two consecutive 16 byte AES blocks) is encrypted with a unique key derived from the flash encryption key. - :esp32s2 or esp32s3: - Flash memory contents is encrypted using XTS-AES-128 or XTS-AES-256. The flash encryption key is 256 bits and 512 bits respectively and stored one or two ``BLOCK_KEYN`` eFuses internal to the chip and, by default, is protected from software access. + :esp32s2 or esp32s3: - Flash memory contents is encrypted using XTS-AES-128 or XTS-AES-256. The flash encryption key is 256 bits and 512 bits respectively and stored in one or two ``BLOCK_KEYN`` eFuses internal to the chip and, by default, is protected from software access. - :esp32c3: - Flash memory contents is encrypted using XTS-AES-128. The flash encryption key is 256 bits and stored one``BLOCK_KEYN`` eFuse internal to the chip and, by default, is protected from software access. + :esp32c3: - Flash memory contents is encrypted using XTS-AES-128. The flash encryption key is 256 bits and stored in one ``BLOCK_KEYN`` eFuse internal to the chip and, by default, is protected from software access. - Flash access is transparent via the flash cache mapping feature of {IDF_TARGET_NAME} - any flash regions which are mapped to the address space will be transparently decrypted when read. @@ -733,6 +730,7 @@ Limitations of Flash Encryption Flash encryption protects firmware against unauthorised readout and modification. It is important to understand the limitations of the flash encryption feature: .. list:: + - Flash encryption is only as strong as the key. For this reason, we recommend keys are generated on the device during first boot (default behaviour). If generating keys off-device, ensure proper procedure is followed and don't share the same key between all production devices. - Not all data is stored encrypted. If storing data on flash, check if the method you are using (library, API, etc.) supports flash encryption. - Flash encryption does not prevent an attacker from understanding the high-level layout of the flash. This is because the same AES key is used for every pair of adjacent 16 byte AES blocks. When these adjacent 16 byte blocks contain identical content (such as empty or padding areas), these blocks will encrypt to produce matching pairs of encrypted blocks. This may allow an attacker to make high-level comparisons between encrypted devices (i.e. to tell if two devices are probably running the same firmware version). @@ -751,8 +749,8 @@ It is recommended to use flash encryption in combination with Secure Boot. Howev .. only:: esp32 - :ref:`Plaintext serial flash updates ` are only possible if the :ref:`Re-flashable ` Secure Boot mode is selected and a Secure Boot key was pre-generated and burned to the {IDF_TARGET_NAME} (refer to :ref:`Secure Boot `). In such configuration, ``idf.py bootloader`` will produce a pre-digested bootloader and secure boot digest file for flashing at offset 0x0. When following the plaintext serial re-flashing steps it is necessary to re-flash this file before flashing other plaintext data. - - :ref:`Re-flashing via Pregenerated Flash Encryption Key ` is still possible, provided the bootloader is not re-flashed. Re-flashing the bootloader requires the same :ref:`Re-flashable ` option to be enabled in the Secure Boot config. + - :ref:`Re-flashing via Pregenerated Flash Encryption Key ` is still possible, provided the bootloader is not re-flashed. Re-flashing the bootloader requires the same :ref:`Re-flashable ` option to be enabled in the Secure Boot config. .. _flash-encryption-advanced-features: @@ -854,7 +852,7 @@ However, before the first boot you can choose to keep any of these features enab The eFuse ``FLASH_CRYPT_CONFIG`` determines the number of bits in the flash encryption key which are "tweaked" with the block offset. For details, see :ref:`flash-encryption-algorithm`. - On the first boot or the firmware bootloader, this value is set to the maximum ``0xF``. + On the first boot of the firmware bootloader, this value is set to the maximum ``0xF``. It is possible to burn this eFuse manually and write protect it before the first boot in order to select different tweak values. However, this is not recommended. @@ -956,7 +954,7 @@ The following sections provide some reference information about the operation of - 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 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. + - To see the full flash encryption algorithm implemented in Python, refer to the ``_flash_encryption_operation()`` function in the ``espsecure.py`` source code. .. only:: SOC_FLASH_ENCRYPTION_XTS_AES_256 @@ -964,9 +962,10 @@ The following sections provide some reference information about the operation of Flash Encryption Algorithm ^^^^^^^^^^^^^^^^^^^^^^^^^^ + - {IDF_TARGET_NAME} use the XTS-AES block cipher mode with 256 bit or 512 bit key size for flash encryption. - - XTS-AES is a block chiper mode specifically designed for disc encryption and addresses the weaknesses other potential modes (e.g. AES-CTR) have for this use case. A detailed description of the XTS-AES algorithm can be found in `IEEE Std 1619-2007 `_. + - XTS-AES is a block cipher mode specifically designed for disc encryption and addresses the weaknesses other potential modes (e.g. AES-CTR) have for this use case. A detailed description of the XTS-AES algorithm can be found in `IEEE Std 1619-2007 `_. - The flash encryption key is stored in one or two ``BLOCK_KEYN`` eFuses and, by default, is protected from further writes or software readout. @@ -978,6 +977,7 @@ The following sections provide some reference information about the operation of Flash Encryption Algorithm ^^^^^^^^^^^^^^^^^^^^^^^^^^ + - {IDF_TARGET_NAME} use the XTS-AES block chiper mode with 256 bit size for flash encryption. - XTS-AES is a block chiper mode specifically designed for disc encryption and addresses the weaknesses other potential modes (e.g. AES-CTR) have for this use case. A detailed description of the XTS-AES algorithm can be found in `IEEE Std 1619-2007 `_. diff --git a/docs/zh_CN/security/flash-encryption.rst b/docs/zh_CN/security/flash-encryption.rst index d0b441e9d7..d9d777a0ca 100644 --- a/docs/zh_CN/security/flash-encryption.rst +++ b/docs/zh_CN/security/flash-encryption.rst @@ -1,534 +1,649 @@ - Flash 加密 ============ +{IDF_TARGET_CRYPT_CNT:default="SPI_BOOT_CRYPT_CNT",esp32="FLASH_CRYPT_CNT"} + +{IDF_TARGET_ESP32_V3_ONLY:default="", esp32="(ESP32 V3 only)"} + +{IDF_TARGET_ENCRYPT_COMMAND:default="espsecure.py encrypt_flash_data --aes_xts", esp32="espsecure.py encrypt_flash_data"} + :link_to_translation:`en:[English]` -本文档将介绍 {IDF_TARGET_NAME} 的 Flash 加密功能,并通过示例展示用户如何在开发及生产过程中使用此功能。本文档旨在引导用户快速入门如何测试及验证 Flash 加密的相关操作。有关 Flash 加密块的详细信息可参见 `{IDF_TARGET_NAME} 技术参考手册`_。 +本文档旨在引导用户快速了解 {IDF_TARGET_NAME} 的 flash 加密功能,通过应用程序代码示例向用户演示如何在开发及生产过程中测试及验证 flash 加密的相关操作。 -.. _{IDF_TARGET_NAME} 技术参考手册: {IDF_TARGET_TRM_CN_URL} 概述 ------ -Flash 加密功能用于加密与 {IDF_TARGET_NAME} 搭载使用的 SPI Flash 中的内容。启用 Flash 加密功能后,物理读取 SPI Flash 便无法恢复大部分 Flash 内容。通过明文数据烧录 {IDF_TARGET_NAME} 可应用加密功能,(若已启用加密功能)引导加载程序会在首次启动时对数据进行加密。 +flash 加密功能用于加密与 {IDF_TARGET_NAME} 搭载使用的片外 flash 中的内容。启用 flash 加密功能后,固件会以明文形式烧录,然后在首次启动时将数据进行加密。因此,物理读取 flash 将无法恢复大部分 flash 内容。 -启用 Flash 加密后,系统将默认加密下列类型的 Flash 数据: +启用 flash 加密后,系统将默认加密下列类型的 flash 数据: - - 引导加载程序 - - 分区表 - - 所有 “app” 类型的分区 +- 固件引导加载程序 +- 分区表 +- 所有 “app” 类型的分区 - 其他类型的 Flash 数据将视情况进行加密: +其他类型的数据将视情况进行加密: - - 安全启动引导加载程序摘要(如果已启用安全启动) - - 分区表中标有“加密”标记的分区 +- 任何在分区表中标有“加密”标志的分区。详情请见 :ref:`encrypted-partition-flag`。 +- 如果启用了安全启动,则可以加密安全启动引导程序摘要(见下文)。 .. only:: esp32 - Flash 加密与 :doc:`安全启动` 功能各自独立,您可以在关闭安全启动的状态下使用 Flash 加密。但是,为了安全的计算机环境,二者应同时使用。在关闭安全启动的状态下,需运行其他配置来确保 Flash 加密的有效性。详细信息可参见 :ref:`flash-encryption-without-secure-boot`。 + :doc:`安全启动` 是一个独立的功能,可以与 flash 加密一起使用,从而创建更安全的环境。 .. important:: - 启用 Flash 加密将限制后续 {IDF_TARGET_NAME} 更新。请务必阅读本文档(包括 :ref:`flash-encryption-limitations`)了解启用 Flash 加密的影响。 + + 对于生产用途,flash 加密仅应在“发布”模式下启用。 + +.. important:: + + 启用 flash 加密将限制后续 {IDF_TARGET_NAME} 更新。在使用 flash 加密功能前,请务必阅读本文档了解其影响。 .. _flash-encryption-efuse: -Flash 加密过程中使用的 eFuse +相关 eFuses ------------------------------ -Flash 加密操作由 {IDF_TARGET_NAME} 上的多个 eFuse 控制。以下是这些 eFuse 列表及其描述: - :: +Flash 加密操作由 {IDF_TARGET_NAME} 上的多个 eFuse 控制。以下是这些 eFuse 列表及其描述,下表中的各 eFuse 名称也在 espefuse.py 工具中使用,为了能在 eFuse API 中使用,请在名称前加上 ``ESP_EFUSE_``,如:esp_efuse_read_field_bit(ESP_EFUSE_DISABLE_DL_ENCRYPT)。 - eFuse 描述 是否可锁定为 默认 - 读取/写入 值 +.. Comment: As text in cells of list-table header rows does not wrap, it is necessary to make 0 header rows and apply bold typeface to the first row. Otherwise, the table goes beyond the html page limits on the right. - .. code-block:: none +.. only:: not SOC_FLASH_ENCRYPTION_XTS_AES - 编码方案 该 2 位宽 eFuse 控制 BLOCK1 中使用的实际 是 0 - 位数,从而获得最终的 256 位 AES 密钥。编码 - 方案值解码如下: - 0: 256 bits - 1: 192 bits - 2: 128 bits - 最终的 AES 密钥根据 FLASH_CRYPT_CONFIG 的 - 值产生。 + .. list-table:: Flash 加密过程中使用的 eFuses + :widths: 25 40 10 + :header-rows: 0 - BLOCK1 存储 AES 密钥的 256 位宽 eFuse 块 是 x + * - **eFuse** + - **描述** + - **位深** + * - ``CODING_SCHEME`` + - 控制用于产生最终 256 位 AES 密钥的 block1 的实际位数。可能的值:``0`` 代表 256 位,``1`` 代表 192 位,``2`` 代表 128 位。最终的 AES 密钥根据 ``FLASH_CRYPT_CONFIG`` 值得出 + - 2 + * - ``flash_encryption`` (block1) + - AES 密钥存储 + - 256 位密钥块 + * - ``FLASH_CRYPT_CONFIG`` + - 控制 AES 加密过程 + - 4 + * - ``DISABLE_DL_ENCRYPT`` + - 设置后,在固件下载模式运行时禁用 flash 加密操作 + - 1 + * - ``DISABLE_DL_DECRYPT`` + - 设置后,在 UART 固件下载模式运行时禁用 flash 解密操作 + - 1 + * - ``{IDF_TARGET_CRYPT_CNT}`` + - 在启动时启用/禁用加密。如果设置了偶数个比特位(0、2、4、6),则在启动时加密 flash。如果设置了奇数个比特位(1、3、5、7),则在启动时不加密 flash + - 7 - FLASH_CRYPT_CONFIG 4 位宽 eFuse,控制 AES 加密进程 是 0xF - download_dis_encrypt 设置后,在 UART 下载模式运行时关闭 Flash 加 是 0 - 密操作 +.. only:: SOC_FLASH_ENCRYPTION_XTS_AES_256 - download_dis_decrypt 设置后,在 UART 下载模式运行时关闭 Flash 解 是 0 - 密操作 + .. list-table:: Flash 加密过程中使用的 eFuses + :widths: 25 40 10 + :header-rows: 0 - FLASH_CRYPT_CNT 7 位 eFuse,在启动时启用/关闭加密功能 是 0 + * - **eFuse** + - **描述** + - **位深** + * - ``BLOCK_KEYN`` + - AES 密钥存储,N 在 0-5 之间 + - XTS_AES_128 有一个 256 位密钥块,XTS_AES_256 有两个 256 位密钥块(共 512 位) + * - ``KEY_PURPOSE_N`` + - 控制 eFuse 块 ``BLOCK_KEYN`` 的目的,其中 N 在 0-5 之间。可能的值:``2`` 代表 ``XTS_AES_256_KEY_1``,``3`` 代表 ``XTS_AES_256_KEY_2``,``4`` 代表 ``XTS_AES_128_KEY``。最终 AES 密钥是基于其中一个或两个目的 eFuses 值推导。有关各种可能的组合,请参阅 *{IDF_TARGET_NAME} 技术参考手册* > *外部内存加密和解密(XTS_AES)* [PDF <{IDF_TARGET_TRM_CN_URL}#extmemencr>__]。 + - 4 + * - ``DIS_DOWNLOAD_MANUAL_ENCRYPT`` + - 设置后,在下载启动模式下禁用 flash 加密 + - 1 + * - ``{IDF_TARGET_CRYPT_CNT}`` + - 设置 SPI 启动模式后,可启用加密和解密。如果在 eFuse 中设置了 1 或 3 个比特位,则启用该功能,否则将禁用。 + - 3 - 偶数位(0,2,4,6): - 启动时加密 Flash - 奇数位(1,3,5,7): - 启动时不加密 Flash +.. only:: SOC_FLASH_ENCRYPTION_XTS_AES and not SOC_FLASH_ENCRYPTION_XTS_AES_256 + + .. list-table:: Flash 加密过程中使用的 eFuses + :widths: 25 40 10 + :header-rows: 0 + + * - **eFuse** + - **描述** + - **位深** + * - ``BLOCK_KEYN`` + - AES 密钥存储,N 在 0-5 之间 + - 256 位密钥块 + * - ``KEY_PURPOSE_N`` + - 控制 eFuse 块 ``BLOCK_KEYN`` 的目的,其中 N 在 0 到 5 之间。对于 flash 加密,唯一的有效值是 4,代表 ``XTS_AES_128_KEY`` + - 4 + * - ``DIS_DOWNLOAD_MANUAL_ENCRYPT`` + - 设置后,则在下载引导模式时禁用 flash 加密 + - 1 + * - ``{IDF_TARGET_CRYPT_CNT}`` + - 设置 SPI 启动模式后,可启用加密和解密。如果在 eFuse 中设置 1 或 3 个比特位,则启用该功能,否则将禁用。 + - 3 + +.. note:: + * 上表中列出的所有 eFuse 位都提供读/写访问控制。 + * 这些位的默认值是 0。 + +对上述 eFuse 位的读写访问由 ``WR_DIS`` 和 ``RD_DIS`` 寄存器中的相应字段控制。有关 {IDF_TARGET_NAME} eFuse 的详细信息,请参考 :doc:`eFuse 管理器 <../api-reference/system/efuse>`。要使用 espefuse.py 更改 eFuse 字段的保护位,请使用以下两个命令:read_protect_efuse 和 write_protect_efuse。例如 ``espefuse.py write_protect_efuse DISABLE_DL_ENCRYPT``。 -对上述位的读写访问由 ``efuse_wr_disable`` 和 ``efuse_rd_disable`` 寄存器中的相应位控制。有关 {IDF_TARGET_NAME} eFuse 的详细信息可参见 :doc:`eFuse 管理器 <../api-reference/system/efuse>`。 Flash 的加密过程 ------------------ -假设 eFuse 值处于默认状态,且第二阶段的引导加载程序编译为支持 Flash 加密,则 Flash 加密过程执行如下: +假设 eFuse 值处于默认状态,且固件的引导加载程序编译为支持 flash 加密,则 flash 加密的具体过程如下: -- 首次上电复位时,Flash 中的所有数据都是未加密形式(明文数据)。第一阶段加载器 (Rom) 将在 IRAM 中加载第二阶段加载器。 -- 第二阶段引导加载程序将读取 flash_crypt_cnt (=00000000b) eFuse 值,因为该值为 0(偶数位),第二阶段引导加载程序将配置并启用 Flash 加密块,同时将 ``FLASH_CRYPT_CFG`` eFuse 的值编程为 0xF。 -- Flash 加密块将生成 AES-256 位密钥,并将其储存于 BLOCK1 eFuse 中。该操作在硬件中执行,软件将无法访问此密钥。 -- 接着,Flash 加密块将加密 Flash 的内容(根据分区表的标记值)。原地加密可能会有耗时(取决于大分区的耗时)。 -- 随后,第二阶段引导加载程序将在 flash_crypt_cnt (=00000001b) 中设置第一个可用位,从而标记已加密的 Flash 内容(偶数位)。 -- 在 :ref:`flash_enc_release_mode` 下,第二阶段引导加载程序将把 ``download_dis_encrypt``、``download_dis_decrypt`` 和 ``download_dis_cache`` 的 eFuse 位改写为 1,防止 UART 引导加载程序解密 Flash 的内容。同时也将写保护 ``FLASH_CRYPT_CNT`` 的 eFuse 位。 -- 在 :ref:`flash_enc_development_mode` 下,第二阶段引导加载程序将仅改写 ``download_dis_decrypt`` 和 ``download_dis_cache`` 的 eFuse 位,从而允许 UART 引导加载程序重新烧录加密的二进制文件。同时将不会写保护 ``FLASH_CRYPT_CNT`` 的 eFuse 位。 -- 然后,第二阶段引导加载程序重启设备并开始执行加密映像,同时将透明解密 Flash 的内容并将其加载至 IRAM。 +.. only:: not SOC_FLASH_ENCRYPTION_XTS_AES -在开发阶段常需编写不同的明文 Flash 映像,以及测试 Flash 的加密过程。这要求 UART 下载模式能够根据需求不断加载新的明文映像。但是,在量产和生产过程中,出于安全考虑,UART 下载模式不应有权限访问 Flash 内容。因此需要有两种不同的 {IDF_TARGET_NAME} 配置:一种用于开发,另一种用于生产。以下章节介绍了 Flash 加密的 :ref:`flash_enc_development_mode` 和 :ref:`flash_enc_release_mode` 及其使用指南。 + 1. 第一次开机复位时,flash 中的所有数据都是未加密的(明文)。ROM 引导加载程序加载固件引导加载程序。 -.. important:: - 顾名思义,开发模式应 **仅开发过程** 使用,因为该模式可以修改和回读加密的 Flash 内容。 + 2. 固件的引导加载程序将读取 ``{IDF_TARGET_CRYPT_CNT}`` eFuse 值(``0b0000000``)。因为该值为 0(偶数位),固件的引导加载程序将配置并启用 flash 加密块,同时将 ``FLASH_CRYPT_CONFIG`` eFuse 的值编程为 0xF。关于 flash 加密块的更多信息,请参考 *{IDF_TARGET_NAME} 技术参考手册* > *eFuse 控制器(eFuse)* > *flash 加密块* [`PDF <{IDF_TARGET_TRM_CN_URL}#efuse>`__]。 -设置 Flash 加密的步骤 + 3. 固件的引导加载程序使用 RNG(随机数生成)模块生成 AES-256 位密钥,然后将其写入 ``flash_encryption`` eFuse 中。由于 ``flash_encryption`` eFuse 已设置编写和读取保护位,将无法通过软件访问密钥。Flash 加密操作完全在硬件中完成,无法通过软件访问密钥。 + + 4. Flash 加密块将加密 flash 的内容(固件的引导加载程序、应用程序、以及标有“加密”标志的分区)。就地加密可能会耗些时间(对于大分区最多需要一分钟)。 + + 5. 固件引导加载程序将在 ``{IDF_TARGET_CRYPT_CNT}`` (0b0000001) 中设置第一个可用位来对已加密的 flash 内容进行标记。设置奇数个比特位。 + + 6. 对于 :ref:`flash-enc-development-mode`,固件引导加载程序仅设置 ``DISABLE_DL_DECRYPT`` 和 ``DISABLE_DL_CACHE`` 的 eFuse 位,以便 UART 引导加载程序重新烧录加密的二进制文件。此外, ``{IDF_TARGET_CRYPT_CNT}`` 的 eFuse 位不受写入保护。 + + 7. 对于 :ref:`flash-enc-release-mode`,固件引导加载程序设置 ``DISABLE_DL_ENCRYPT``、``DISABLE_DL_DECRYPT`` 和 ``DISABLE_DL_CACHE`` 的 eFuse 位为 1,以防止 UART 引导加载程序解密 flash 内容。它还写保护 ``{IDF_TARGET_CRYPT_CNT}`` eFuse 位。要修改此行为,请参阅 :ref:`uart-bootloader-encryption`。 + + 8. 重新启动设备以开始执行加密镜像。固件引导加载程序调用 flash 解密块来解密 flash 内容,然后将解密的内容加载到 IRAM 中。 + +.. only:: SOC_FLASH_ENCRYPTION_XTS_AES_256 + + 1. 第一次开机复位时,flash 中的所有数据都是未加密的(明文)。ROM 引导加载程序加载固件引导加载程序。 + + 2. 固件的引导加载程序将读取 ``{IDF_TARGET_CRYPT_CNT}`` eFuse 值(``0b000``)。因为该值为 0(偶数位),固件引导加载程序将配置并启用 flash 加密块。关于 flash 加密块的更多信息,请参考 *{IDF_TARGET_NAME} 技术参考手册* > *eFuse 控制器(eFuse)* > *自动加密块* [`PDF <{IDF_TARGET_TRM_CN_URL}#efuse>`__]。 + + 3. 固件的引导加载程序使用 RNG(随机数生成)模块生成 256 位或 512 位密钥,具体取决于 :ref:`生成的 AES-XTS 密钥的大小 `,然后分别将其写入一个或两个 `BLOCK_KEYN` eFuses。软件也为存储密钥的块更新了 ``KEY_PURPOSE_N``。由于一或两个 ``BLOCK_KEYN`` eFuse 已设置编写和读取保护位,将无法通过软件访问密钥。``KEY_PURPOSE_N`` 字段也受写保护。Flash 加密操作完全在硬件中完成,无法通过软件访问密钥。 + + 4. Flash 加密块将加密 flash 的内容(固件的引导加载程序、应用程序、以及标有“加密”标志的分区)。就地加密可能会耗些时间(对于大分区最多需要一分钟)。 + + 5. 固件引导加载程序将在 ``{IDF_TARGET_CRYPT_CNT}`` (0b001) 中设置第一个可用位来对已加密的 flash 内容进行标记。设置奇数位。 + + 6. 对于 :ref:`flash-enc-development-mode`,固件引导加载程序允许 UART 引导加载程序重新烧录加密后的二进制文件。同时,``{IDF_TARGET_CRYPT_CNT}`` eFuse 位不受写入保护。此外,默认情况下,固件引导加载程序设置 ``DIS_BOOT_REMAP``、 ``DIS_DOWNLOAD_ICACHE``、 ``DIS_DOWNLOAD_DCACHE``、 ``HARD_DIS_JTAG`` 和 ``DIS_LEGACY_SPI_BOOT`` eFuse 位。 + + 7. 对于 :ref:`flash-enc-release-mode`,固件引导加载程序设置所有在开发模式下设置的 eFuse 位。它还写保护 ``{IDF_TARGET_CRYPT_CNT}`` eFuse 位。要修改此行为,请参阅 :ref:`uart-bootloader-encryption`。 + + 8. 重新启动设备以开始执行加密镜像。固件引导加载程序调用 flash 解密块来解密 flash 内容,然后将解密的内容加载到 IRAM 中。 + +.. only:: SOC_FLASH_ENCRYPTION_XTS_AES and not SOC_FLASH_ENCRYPTION_XTS_AES_256 + + 1. 第一次开机复位时,flash 中的所有数据都是未加密的(明文)。ROM 引导加载程序加载固件引导加载程序。 + + 2. 固件的引导加载程序将读取 ``{IDF_TARGET_CRYPT_CNT}`` eFuse 值(``0b000``)。因为该值为 0(偶数位),固件引导加载程序将配置并启用 flash 加密块。关于 flash 加密块的更多信息,请参考 `{IDF_TARGET_NAME} 技术参考手册 <{IDF_TARGET_TRM_CN_URL}>`_。 + + 3. 固件的引导加载程序使用 RNG(随机数生成)模块生成 256 位密钥,然后将其写入 `BLOCK_KEYN` eFuse。软件也为存储密钥的块更新了 ``KEY_PURPOSE_N``。由于 ``BLOCK_KEYN`` eFuse 已设置编写和读取保护位,将无法通过软件访问密钥。``KEY_PURPOSE_N`` 字段也受写保护。Flash 加密操作完全在硬件中完成,无法通过软件访问密钥。 + + 4. Flash 加密块将加密 flash 的内容(固件的引导加载程序、应用程序、以及标有“加密”标志的分区)。就地加密可能会耗些时间(对于大分区最多需要一分钟)。 + + 5. 固件引导加载程序将在 ``{IDF_TARGET_CRYPT_CNT}`` (0b001) 中设置第一个可用位来对已加密的 flash 内容进行标记。设置奇数位。 + + 6. 对于 :ref:`flash-enc-development-mode`,固件引导加载程序允许 UART 引导加载程序重新烧录加密后的二进制文件。同时,``{IDF_TARGET_CRYPT_CNT}`` eFuse 位不受写入保护。此外,默认情况下,固件引导加载程序设置 ``DIS_DOWNLOAD_ICACHE``、 ``DIS_PAD_JTAG``、 ``DIS_USB_JTAG`` 和 ``DIS_LEGACY_SPI_BOOT`` eFuse 位。 + + 7. 对于 :ref:`flash-enc-release-mode`,固件引导加载程序设置所有在开发模式下设置的 eFuse 位以及 ``DIS_DOWNLOAD_MANUAL_ENCRYPT``。它还写保护 ``{IDF_TARGET_CRYPT_CNT}`` eFuse 位。要修改此行为,请参阅 :ref:`uart-bootloader-encryption`。 + + 8. 重新启动设备以开始执行加密镜像。固件引导加载程序调用 flash 解密块来解密 flash 内容,然后将解密的内容加载到 IRAM 中。 + +在开发阶段常需编写不同的明文 flash 镜像并测试 flash 的加密过程。这要求固件下载模式能够根据需求不断加载新的明文镜像。但是,在制造和生产过程中,出于安全考虑,固件下载模式不应有权限访问 flash 内容。 + +因此需要有两种不同的 flash 加密配置:一种用于开发,另一种用于生产。详情请参考 :ref:`flash-encryption-configuration`。 + +.. _flash-encryption-configuration: + +Flash 加密设置 ---------------------- -.. _flash_enc_development_mode: +提供以下 flash 加密模式: + +- :ref:`flash-enc-development-mode` - 建议仅在开发过程中使用,因为它不会阻止修改和读取加密的 flash 内容。 +- :ref:`flash-enc-release-mode` - 建议用于制造和生产,以防止物理读取加密的 flash 内容。 + +本节将详细介绍上述 flash 加密模式,并且逐步说明如何使用它们。 + +.. _flash-enc-development-mode: 开发模式 ^^^^^^^^^^ -可使用 {IDF_TARGET_NAME} 内部生成的密钥或外部主机生成的密钥在开发中运行 Flash 加密。 - -使用 {IDF_TARGET_NAME} 生成的 Flash 加密密钥 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -正如上文所说,:ref:`flash_enc_development_mode` 允许用户使用 UART 下载模式多次下载明文映像。需完成以下步骤测试 Flash 加密过程: - -- 确保您的 {IDF_TARGET_NAME} 设备有 :ref:`flash-encryption-efuse` 中所示的 Flash 加密 eFuse 的默认设置。 - -- 可在 ``$IDF_PATH/examples/security/flash_encryption`` 文件夹中找到 Flash 加密的示例应用程序。该示例应用程序中有显示 Flash 加密的状态(启用或关闭)以及 ``FLASH_CRYPT_CNT`` eFuse 值。 - -- 在第二阶段引导加载程序中启用 Flash 加密支持。请前往 :ref:`project-configuration-menu`,选择 "Security Features"。 - -- 选择 :ref:`Enable flash encryption on boot `。 - -- 默认设置模式为 **开发模式**。 - -- 在引导加载程序 config 下选择适当详细程度的日志。 - -.. only:: esp32 - - - 启用 Flash 加密将增大引导加载程序,因而可能需更新分区表偏移。请参见 :ref:`bootloader-size`。 - -- 保存配置并退出。 - -构建并烧录完整的映像包括:引导加载程序、分区表和 app。这些分区最初以未加密形式写入 Flash。 - - :: - - idf.py flash monitor - -一旦烧录完成,设备将重置,在下次启动时,第二阶段引导加载程序将加密 Flash 的 app 分区,然后重置该分区。现在,示例应用程序将在运行时解密并执行命令。以下是首次启用 Flash 加密后 {IDF_TARGET_NAME} 启动时的样例输出。 - - :: - - --- idf_monitor on /dev/cu.SLAB_USBtoUART 115200 --- - --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- - ets Jun 8 2016 00:22:57 - - rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) - configsip: 0, SPIWP:0xee - clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 - mode:DIO, clock div:2 - load:0x3fff0018,len:4 - load:0x3fff001c,len:8452 - load:0x40078000,len:13608 - load:0x40080400,len:6664 - entry 0x40080764 - I (28) boot: ESP-IDF v4.0-dev-850-gc4447462d-dirty 2nd stage bootloader - I (29) boot: compile time 15:37:14 - I (30) boot: Enabling RNG early entropy source... - I (35) boot: SPI Speed : 40MHz - I (39) boot: SPI Mode : DIO - I (43) boot: SPI Flash Size : 4MB - I (47) boot: Partition Table: - I (51) boot: ## Label Usage Type ST Offset Length - I (58) boot: 0 nvs WiFi data 01 02 0000a000 00006000 - I (66) boot: 1 phy_init RF data 01 01 00010000 00001000 - I (73) boot: 2 factory factory app 00 00 00020000 00100000 - I (81) boot: End of partition table - I (85) esp_image: segment 0: paddr=0x00020020 vaddr=0x3f400020 size=0x0808c ( 32908) map - I (105) esp_image: segment 1: paddr=0x000280b4 vaddr=0x3ffb0000 size=0x01ea4 ( 7844) load - I (109) esp_image: segment 2: paddr=0x00029f60 vaddr=0x40080000 size=0x00400 ( 1024) load - 0x40080000: _WindowOverflow4 at esp-idf/esp-idf/components/freertos/xtensa_vectors.S:1778 - - I (114) esp_image: segment 3: paddr=0x0002a368 vaddr=0x40080400 size=0x05ca8 ( 23720) load - I (132) esp_image: segment 4: paddr=0x00030018 vaddr=0x400d0018 size=0x126a8 ( 75432) map - 0x400d0018: _flash_cache_start at ??:? - - I (159) esp_image: segment 5: paddr=0x000426c8 vaddr=0x400860a8 size=0x01f4c ( 8012) load - 0x400860a8: prvAddNewTaskToReadyList at esp-idf/esp-idf/components/freertos/tasks.c:4561 - - I (168) boot: Loaded app from partition at offset 0x20000 - I (168) boot: Checking flash encryption... - I (168) flash_encrypt: Generating new flash encryption key... - I (187) flash_encrypt: Read & write protecting new key... - I (187) flash_encrypt: Setting CRYPT_CONFIG efuse to 0xF - W (188) flash_encrypt: Not disabling UART bootloader encryption - I (195) flash_encrypt: Disable UART bootloader decryption... - I (201) flash_encrypt: Disable UART bootloader MMU cache... - I (208) flash_encrypt: Disable JTAG... - I (212) flash_encrypt: Disable ROM BASIC interpreter fallback... - I (219) esp_image: segment 0: paddr=0x00001020 vaddr=0x3fff0018 size=0x00004 ( 4) - I (227) esp_image: segment 1: paddr=0x0000102c vaddr=0x3fff001c size=0x02104 ( 8452) - I (239) esp_image: segment 2: paddr=0x00003138 vaddr=0x40078000 size=0x03528 ( 13608) - I (249) esp_image: segment 3: paddr=0x00006668 vaddr=0x40080400 size=0x01a08 ( 6664) - I (657) esp_image: segment 0: paddr=0x00020020 vaddr=0x3f400020 size=0x0808c ( 32908) map - I (669) esp_image: segment 1: paddr=0x000280b4 vaddr=0x3ffb0000 size=0x01ea4 ( 7844) - I (672) esp_image: segment 2: paddr=0x00029f60 vaddr=0x40080000 size=0x00400 ( 1024) - 0x40080000: _WindowOverflow4 at esp-idf/esp-idf/components/freertos/xtensa_vectors.S:1778 - - I (676) esp_image: segment 3: paddr=0x0002a368 vaddr=0x40080400 size=0x05ca8 ( 23720) - I (692) esp_image: segment 4: paddr=0x00030018 vaddr=0x400d0018 size=0x126a8 ( 75432) map - 0x400d0018: _flash_cache_start at ??:? - - I (719) esp_image: segment 5: paddr=0x000426c8 vaddr=0x400860a8 size=0x01f4c ( 8012) - 0x400860a8: prvAddNewTaskToReadyList at esp-idf/esp-idf/components/freertos/tasks.c:4561 - - I (722) flash_encrypt: Encrypting partition 2 at offset 0x20000... - I (13229) flash_encrypt: Flash encryption completed - I (13229) boot: Resetting with flash encryption enabled... +在开发过程中,可使用 {IDF_TARGET_NAME} 内部生成的密钥或外部主机生成的密钥进行 flash 加密。 - 启用 Flash 加密后,在下次启动时输出将显示已启用 Flash 加密。 +使用 {IDF_TARGET_NAME} 生成的密钥 +""""""""""""""""""""""""""""""""""""" - :: +开发模式允许用户使用固件下载模式下载多个明文镜像。 - rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) - configsip: 0, SPIWP:0xee - clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 - mode:DIO, clock div:2 - load:0x3fff0018,len:4 - load:0x3fff001c,len:8452 - load:0x40078000,len:13652 - ho 0 tail 12 room 4 - load:0x40080400,len:6664 - entry 0x40080764 - I (30) boot: ESP-IDF v4.0-dev-850-gc4447462d-dirty 2nd stage bootloader - I (30) boot: compile time 16:32:53 - I (31) boot: Enabling RNG early entropy source... - I (37) boot: SPI Speed : 40MHz - I (41) boot: SPI Mode : DIO - I (45) boot: SPI Flash Size : 4MB - I (49) boot: Partition Table: - I (52) boot: ## Label Usage Type ST Offset Length - I (60) boot: 0 nvs WiFi data 01 02 0000a000 00006000 - I (67) boot: 1 phy_init RF data 01 01 00010000 00001000 - I (75) boot: 2 factory factory app 00 00 00020000 00100000 - I (82) boot: End of partition table - I (86) esp_image: segment 0: paddr=0x00020020 vaddr=0x3f400020 size=0x0808c ( 32908) map - I (107) esp_image: segment 1: paddr=0x000280b4 vaddr=0x3ffb0000 size=0x01ea4 ( 7844) load - I (111) esp_image: segment 2: paddr=0x00029f60 vaddr=0x40080000 size=0x00400 ( 1024) load - 0x40080000: _WindowOverflow4 at esp-idf/esp-idf/components/freertos/xtensa_vectors.S:1778 +测试 flash 加密过程需完成以下步骤: - I (116) esp_image: segment 3: paddr=0x0002a368 vaddr=0x40080400 size=0x05ca8 ( 23720) load - I (134) esp_image: segment 4: paddr=0x00030018 vaddr=0x400d0018 size=0x126a8 ( 75432) map - 0x400d0018: _flash_cache_start at ??:? +1. 确保您的 {IDF_TARGET_NAME} 设备有 :ref:`flash-encryption-efuse` 中所示的 flash 加密 eFuse 的默认设置。 - I (162) esp_image: segment 5: paddr=0x000426c8 vaddr=0x400860a8 size=0x01f4c ( 8012) load - 0x400860a8: prvAddNewTaskToReadyList at esp-idf/esp-idf/components/freertos/tasks.c:4561 + 请参考如何检查 :ref:`flash-encryption-status`。 - I (171) boot: Loaded app from partition at offset 0x20000 - I (171) boot: Checking flash encryption... - I (171) flash_encrypt: flash encryption is enabled (3 plaintext flashes left) - I (178) boot: Disabling RNG early entropy source... - I (184) cpu_start: Pro cpu up. - I (188) cpu_start: Application information: - I (193) cpu_start: Project name: flash-encryption - I (198) cpu_start: App version: v4.0-dev-850-gc4447462d-dirty - I (205) cpu_start: Compile time: Jun 17 2019 16:32:52 - I (211) cpu_start: ELF file SHA256: 8770c886bdf561a7... - I (217) cpu_start: ESP-IDF: v4.0-dev-850-gc4447462d-dirty - I (224) cpu_start: Starting app cpu, entry point is 0x40080e4c - 0x40080e4c: call_start_cpu1 at esp-idf/esp-idf/components/{IDF_TARGET_PATH_NAME}/cpu_start.c:265 +2. 在 :ref:`项目配置菜单 `,执行以下操作: - I (0) cpu_start: App cpu up. - I (235) heap_init: Initializing. RAM available for dynamic allocation: - I (241) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM - I (247) heap_init: At 3FFB2EC8 len 0002D138 (180 KiB): DRAM - I (254) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM - I (260) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM - I (266) heap_init: At 40087FF4 len 0001800C (96 KiB): IRAM - I (273) cpu_start: Pro cpu start user code - I (291) cpu_start: Starting scheduler on PRO CPU. - I (0) cpu_start: Starting scheduler on APP CPU. + .. list:: - Sample program to check Flash Encryption - This is {IDF_TARGET_NAME} chip with 2 CPU cores, WiFi/BT/BLE, silicon revision 1, 4MB external flash - Flash encryption feature is enabled - Flash encryption mode is DEVELOPMENT - Flash in encrypted mode with flash_crypt_cnt = 1 - Halting... + - :ref:`启动时使能 flash 加密 ` + - :ref:`选择加密模式 ` (默认是 **开发模式**) + :esp32: - :ref:`选择 UART ROM 下载模式 ` (默认是 **启用**。 对于 esp32 芯片,该选项仅在 :ref:`CONFIG_ESP32_REV_MIN` 级别设置为 3 时 (ESP32 V3) 可用)。 + :not esp32: - :ref:`选择 UART ROM 下载模式 ` (默认是 **启用**) + :esp32s2 or esp32s3: - 设置 :ref:`生成的 AES-XTS 密钥大小 ` + - :ref:`选择适当详细程度的引导加载程序日志 ` + - 保存配置并退出。 -在此阶段,如果用户希望以加密格式将已修改的明文应用程序映像更新到 Flash 中,可使用以下命令: +启用 flash 加密将增大引导加载程序,因而可能需更新分区表偏移量。请参考 :ref:`引导加载程序大小 `。 - :: +3. 运行以下命令来构建和烧录完整的镜像。 - idf.py encrypted-app-flash monitor + .. code-block:: bash -.. _encrypt_partitions: + idf.py flash monitor -加密多重分区 -^^^^^^^^^^^^^ + .. note:: -如果所有分区都需以加密格式更新,则可使用以下命令: + 这个命令不包括任何应该写入 flash 分区的用户文件。请在运行此命令前手动写入这些文件,否则在写入前应单独对这些文件进行加密。 - :: +该命令将向 flash 写入未加密的镜像:固件引导加载程序、分区表和应用程序。烧录完成后,{IDF_TARGET_NAME} 将重置。在下一次启动时,固件引导加载程序会加密:固件引导加载程序、应用程序分区和标记为“加密”的分区,然后复位。就地加密可能需要时间,对于大分区最多需要一分钟。之后,应用程序在运行时解密并执行命令。 + +下面是启用 flash 加密后 {IDF_TARGET_NAME} 首次启动时的样例输出: + +.. include:: {IDF_TARGET_PATH_NAME}_log.inc + :start-after: first_boot_enc + :end-before: ------ + +启用 flash 加密后,在下次启动时输出将显示已启用 flash 加密,样例输出如下: + +.. include:: {IDF_TARGET_PATH_NAME}_log.inc + :start-after: already_en_enc + :end-before: ------ + +在此阶段,如果用户需要更新或重新烧录二进制文件,请参考 :ref:`encrypt-partitions`。 - idf.py encrypted-flash monitor .. _pregenerated-flash-encryption-key: -使用主机生成的 Flash 加密密钥 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +使用主机生成的密钥 +"""""""""""""""""""""""" -可在主机中预生成 Flash 加密密钥,并将其烧录到 {IDF_TARGET_NAME} 的 eFuse 密钥块中。这样,无需明文 Flash 更新便可以在主机上预加密数据并将其烧录到 {IDF_TARGET_NAME} 中。该功能允许在 :ref:`flash_enc_development_mode` 和 :ref:`flash_enc_release_mode` modes 两模式下加密烧录。 +可在主机中预生成 flash 加密密钥,并将其烧录到 eFuse 密钥块中。这样,无需明文 flash 更新便可以在主机上预加密数据并将其烧录。该功能可在 :ref:`flash-enc-development-mode` 和 :ref:`flash-enc-release-mode` 两模式下使用。如果没有预生成的密钥,数据将以明文形式烧录,然后 {IDF_TARGET_NAME} 对数据进行就地加密。 -- 确保您的 {IDF_TARGET_NAME} 设备有 :ref:`flash-encryption-efuse` 中所示 Flash 加密 eFuse 的默认设置。 +.. note:: -- 使用 espsecure.py 随机生成一个密钥:: + 不建议在生产中使用该方法,除非为每个设备都单独生成一个密钥。 - espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin +使用主机生成的密钥需完成以下步骤: -- 将该密钥烧录到设备上(一次性)。 **该步骤须在第一次加密启动前完成**,否则 {IDF_TARGET_NAME} 将随机生成一个软件无权限访问或修改的密钥:: +1. 确保您的 {IDF_TARGET_NAME} 设备有 :ref:`flash-encryption-efuse` 中所示的 flash 加密 eFuse 的默认设置。 + + 请参考如何检查 :ref:`flash-encryption-status`。 + +2. 通过运行以下命令生成一个随机密钥: + +.. only:: esp32s2 + + 如果 :ref:`生成的 AES-XTS 密钥大小 ` 是 AES-256(512 位密钥),则需要使用 `XTS_AES_256_KEY_1` 和 `XTS_AES_256_KEY_2`。espsecure 不支持 512 位密钥,但可以变通一下。 + + .. code-block:: bash + + espsecure.py generate_flash_encryption_key my_flash_encryption_key1.bin + + espsecure.py generate_flash_encryption_key my_flash_encryption_key2.bin + + # To use encrypt_flash_data with XTS_AES_256 requires combining the two binary files to one 64 byte file + cat my_flash_encryption_key1.bin my_flash_encryption_key2.bin > my_flash_encryption_key.bin + + 如果 :ref:`生成的 AES-XTS 密钥大小 ` 是 AES-128(256 位密钥),则需要使用 `XTS_AES_128_KEY`。 + + .. code-block:: bash + + espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin + + +.. only:: not SOC_FLASH_ENCRYPTION_XTS_AES_256 + + .. code-block:: bash + + espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin + +3. **在第一次加密启动前**,使用以下命令将该密钥烧录到设备上,这个操作只能执行 **一次**。 + +.. only:: not SOC_FLASH_ENCRYPTION_XTS_AES + + .. code-block:: bash espefuse.py --port PORT burn_key flash_encryption my_flash_encryption_key.bin -- 在第二阶段引导加载程序中启用 Flash 加密支持。请前往 :ref:`project-configuration-menu`,选择 “Security Features”。 +.. only:: SOC_FLASH_ENCRYPTION_XTS_AES_256 -- 选择 :ref:`Enable flash encryption on boot `。 + .. code-block:: bash -- 模式默认设置为 **开发模式**。 + espefuse.py --port PORT burn_key BLOCK my_flash_encryption_key.bin KEYPURPOSE + + 其中 ``BLOCK`` 是 ``BLOCK_KEY0`` 和 ``BLOCK_KEY5`` 之间的空闲密钥区。而 ``KEYPURPOSE`` 是 ``AES_256_KEY_1``、``XTS_AES_256_KEY_2`` 或 ``XTS_AES_128_KEY``。关于密钥目的的描述清参考 `{IDF_TARGET_NAME} 技术参考手册 <{IDF_TARGET_TRM_CN_URL}>`_。 -- 在引导加载程序 config 下选择适当详细程度的日志。 + AES-128(256 位密钥)- ``XTS_AES_128_KEY``: -.. only:: esp32 + .. code-block:: bash - - 启用 Flash 加密将增大引导加载程序,因而可能需要更新分区表偏移。可参见 See :ref:`bootloader-size`。 + espefuse.py --port PORT burn_key BLOCK my_flash_encryption_key.bin XTS_AES_128_KEY -- 保存配置并退出。 + AES-256(512 位密钥)- ``XTS_AES_256_KEY_1`` 和 ``XTS_AES_256_KEY_2``。espefuse.py 和 espsecure.py 中还没有完全支持。需要进行如下操作: -构建并烧录完整的映像包括:引导加载程序、分区表和 app。这些分区最初以未加密形式写入 Flash + .. code-block:: bash - :: + espefuse.py --port PORT burn_key BLOCK my_flash_encryption_key1.bin XTS_AES_256_KEY_1 - idf.py flash monitor + espefuse.py --port PORT burn_key BLOCK+1 my_flash_encryption_key2.bin XTS_AES_256_KEY_2 -下次启动时,第二阶段引导加载程序将加密 Flash 的 app 分区并重置该分区。现在,示例应用程序将在运行时解密并执行命令。 +.. only:: SOC_FLASH_ENCRYPTION_XTS_AES and not SOC_FLASH_ENCRYPTION_XTS_AES_256 -在此阶段,如果用户希望将新的明文应用程序映像更新到 Flash,应调用以下命令 + .. code-block:: bash - :: + espefuse.py --port PORT burn_key BLOCK my_flash_encryption_key.bin XTS_AES_128_KEY - idf.py encrypted-app-flash monitor + 其中 ``BLOCK`` 是 ``BLOCK_KEY0`` 和 ``BLOCK_KEY5`` 之间的一个空闲密钥区。 -如何以加密格式重新编程所有分区,可参考 :ref:`encrypt_partitions`。 +如果未烧录密钥并在启用 flash 加密后启动设备,{IDF_TARGET_NAME} 将生成随机密钥,该密钥软件无法访问或修改。 -.. _flash_enc_release_mode: +4. 在 :ref:`项目配置菜单 ` 中进行如下设置: -释放模式 + - :ref:`启动时启用 flash 加密功能 ` + - :ref:`选择加密模式 ` (默认为 **开发模式**) + - :ref:`选择适当详细程度的引导加载程序日志 ` + - 保存配置并退出 + +启用 flash 加密将增大引导加载程序,因而可能需更新分区表偏移量。请参考 :ref:`引导加载程序大小 `。 + +5. 运行以下命令来构建并烧录完整的镜像: + + .. code-block:: bash + + idf.py flash monitor + + .. note:: + + 这个命令不包括任何应该被写入 flash 分区的用户文件。请在运行此命令前手动写入这些文件,否则在写入前应单独对这些文件进行加密。 + + 该命令将向 flash 写入未加密的镜像:固件引导加载程序、分区表和应用程序。烧录完成后,{IDF_TARGET_NAME} 将重置。在下一次启动时,固件引导加载程序会加密:固件引导加载程序、应用程序分区和标记为 ``加密`` 的分区,然后复位。就地加密可能需要时间,对于大的分区来说可能耗时一分钟。之后,应用程序在运行时被解密并执行。 + +如果使用开发模式,那么更新和重新烧录二进制文件最简单的方法是 :ref:`encrypt-partitions`。 + +如果使用发布模式,那么可以在主机上预先加密二进制文件,然后将其作为密文烧录。具体请参考 :ref:`manual-encryption`。 + + +.. _encrypt-partitions: + +重新烧录更新后的分区 +"""""""""""""""""""""""""""""" + +如果用户以明文方式更新了应用程序代码并需要重新烧录,则需要在烧录前对其进行加密。请运行以下命令一次完成应用程序的加密与烧录: + +.. code-block:: bash + + idf.py encrypted-app-flash monitor + +如果所有分区都需要以加密形式更新,请运行: + +.. code-block:: bash + + idf.py encrypted-flash monitor + + +.. _flash-enc-release-mode: + +发布模式 ^^^^^^^^^^ -在释放模式下,UART 引导加载程序无法执行 Flash 加密操作,**只能** 使用 OTA 方案下载新的明文映像,该方案将在写入 Flash 前加密明文映像。 +在发布模式下,UART 引导加载程序无法执行 flash 加密操作,**只能** 使用 OTA 方案下载新的明文镜像,该方案将在写入 flash 前加密明文镜像。 -- 确保您的 {IDF_TARGET_NAME} 设备有 :ref:`flash-encryption-efuse` 中所示 Flash 加密 eFuse 的默认设置。 +使用该模式需要执行以下步骤: -- 在第二阶段引导加载程序中启用 Flash 加密支持。请前往 :ref:`project-configuration-menu`,选择 “Security Features”。 +1. 确保您的 {IDF_TARGET_NAME} 设备有 :ref:`flash-encryption-efuse` 中所示的 flash 加密 eFuse 的默认设置。 -- 选择 :ref:`Enable flash encryption on boot `。 + 请参考如何检查 :ref:`flash-encryption-status`。 -- 选择 **释放模式**,模式默认设置为 **开发模式**。请注意,**一旦选择了释放模式,``download_dis_encrypt`` 和 ``download_dis_decrypt`` eFuse 位将被编程为禁止 UART 引导加载程序访问 Flash 的内容**。 +2. 在 :ref:`项目配置菜单 `,执行以下操作: -- 在引导加载程序 config 下选择适当详细程度的日志。 + .. list:: -.. only:: esp32 + - :ref:`启动时使能 flash 加密 ` + :esp32: - :ref:`选择发布模式 ` (注意一旦选择了发布模式,``DISABLE_DL_ENCRYPT`` 和 ``DISABLE_DL_DECRYPT`` eFuse 位将被编程为在 ROM 下载模式下禁用 flash 加密硬件) + :esp32: - :ref:`选择 UART ROM 下载模式(推荐永久性禁用)` (注意该选项仅在 :ref:`CONFIG_ESP32_REV_MIN` 级别设置为 3 时 (ESP32 V3) 可用。)默认选项是保持启用 UART ROM 下载模式,然而建议永久禁用该模式,以减少攻击者可用的选项。 + :not esp32: - :ref:`选择发布模式 ` (注意一旦选择了发布模式,``EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT`` eFuse 位将被编程为在 ROM 下载模式下禁用 flash 加密硬件。) + :not esp32: - :ref:`选择 UART ROM 下载(推荐永久性的切换到安全模式)`。这是默认且推荐使用的选项。如果不需要该模式,也可以改变此配置设置永久地禁用 UART ROM 下载模式。 + - :ref:`选择适当详细程度的引导加载程序日志 ` + - 保存配置并退出 - - 启用 Flash 加密将增大引导加载程序,因而可能需要更新分区表偏移。可参见 See :ref:`bootloader-size`。 +启用 flash 加密将增大引导加载程序,因而可能需更新分区表偏移量。请参考 :ref:`引导加载程序大小 `。 -- 保存配置并退出。 +3. 运行以下命令来构建并烧录完整的镜像: -构建并烧录完整的映像包括:引导加载程序、分区表和 app。这些分区最初以未加密形式写入 Flash + .. code-block:: bash - :: + idf.py flash monitor - idf.py flash monitor -下次启动时,第二阶段引导加载程序将加密 Flash app 分区并重置该分区。现在,示例应用程序应正确执行命令。 + .. note:: -一旦在释放模式下启用 Flash 加密,引导加载程序将写保护 ``FLASH_CRYPT_CNT`` eFuse。 + 这个命令不包括任何应该被写入 flash 分区的用户文件。请在运行此命令前手动写入这些文件,否则在写入前应单独对这些文件进行加密。 + + 该命令将向 flash 写入未加密的镜像:固件引导加载程序、分区表和应用程序。烧录完成后,{IDF_TARGET_NAME} 将重置。在下一次启动时,固件引导加载程序会加密:固件引导加载程序、应用程序分区和标记为 ``加密`` 的分区,然后复位。就地加密可能需要时间,对于大的分区来说可能耗时一分钟。之后,应用程序在运行时被解密并执行。 -应使用 OTA 方案对字段中的明文进行后续更新。详情可参见 :doc:`OTA <../api-reference/system/ota>`。 +一旦在发布模式下启用 flash 加密,引导加载程序将写保护 ``{IDF_TARGET_CRYPT_CNT}`` eFuse。 + +请使用 :ref:`OTA 方案 ` 对字段中的明文进行后续更新。 + +.. note:: + + 如果用户已经预先生成了 flash 加密密钥并存储了一个副本,并且 UART 下载模式没有通过 :ref:`CONFIG_SECURE_UART_ROM_DL_MODE` {IDF_TARGET_ESP32_V3_ONLY} 永久禁用,那么可以通过使用 ``{IDF_TARGET_ENCRYPT_COMMAND}`` 预加密文件,从而在在本地更新 flash,然后烧录密文。请参考 :ref:`manual-encryption`。 + +.. _flash-encrypt-best-practices: + +最佳实践 +^^^^^^^^^^^^^^ + +在生产中使用 flash 加密时: + +.. list:: + + - 不要在多个设备之间重复使用同一个 flash 加密密钥。这样做意味着攻击者从一台设备上复制加密数据后,无法将其转移到第二台设备上。 + :esp32: - 在使用 ESP32 V3 时,如果生产设备不需要 UART ROM 下载模式,那么则该禁用该模式以增加设备安全性。这可以通过在应用程序启动时调用 :cpp:func:`esp_efuse_disable_rom_download_mode` 来实现。或者,可将项目 :ref:`CONFIG_ESP32_REV_MIN` 级别配置为 3(仅针对 ESP32 V3),然后选择 :ref:`CONFIG_SECURE_UART_ROM_DL_MODE` 为“永久性的禁用 ROM 下载模式(推荐)”。在早期的 ESP32 版本上无法禁用 ROM 下载模式。 + :not esp32: - 如果不需要 UART ROM 下载模式,则应完全禁用该模式,或者永久设置为“安全下载模式”。安全下载模式永久性地将可用的命令限制在基本的 flash 只读和只写。默认在发布模式下第一次启动时设置为安全下载模式。要完全禁用下载模式,请选择 :ref:`CONFIG_SECURE_UART_ROM_DL_MODE` 为“永久禁用 ROM 下载模式(推荐)”或在运行时调用 :cpp:func:`esp_efuse_disable_rom_download_mode`。 + - 启用 :doc:`安全启动` 作为额外的保护层,防止攻击者在启动前有选择地破坏 flash 中某部分。 可能出现的错误 -^^^^^^^^^^^^^^^^ +----------------- -启用 Flash 加密后,如果 ``FLASH_CRYPT_CNT`` eFuse 值中有奇数位,则所有(标有加密标志的)分区都应包含加密密文。以下为 {IDF_TARGET_NAME} 加载明文数据会产生的三种典型错误情况: +一旦启用 flash 加密,``{IDF_TARGET_CRYPT_CNT}`` 的 eFuse 值将设置为奇数位。这意味着所有标有加密标志的分区都会包含加密的密本。如果 {IDF_TARGET_NAME} 错误地加载了明文数据,则会出现以下三种典型的错误情况: -1. 如果通过明文引导加载程序映像重新更新了引导加载程序分区,则 ROM 加载器将无法加载 引导加载程序,并会显示以下错误类型: - - :: - - rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) - flash read err, 1000 - ets_main.c 371 - ets Jun 8 2016 00:22:57 - - rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) - flash read err, 1000 - ets_main.c 371 - ets Jun 8 2016 00:22:57 - - rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) - flash read err, 1000 - ets_main.c 371 - ets Jun 8 2016 00:22:57 - - rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) - flash read err, 1000 - ets_main.c 371 - ets Jun 8 2016 00:22:57 - - rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) - flash read err, 1000 - ets_main.c 371 - ets Jun 8 2016 00:22:57 - -2. 如果引导加载程序已加密,但使用明文分区表映像重新更新了分区表,则引导加载程序将无法读取分区表,并会显示以下错误类型: - - :: - - rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) - configsip: 0, SPIWP:0xee - clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 - mode:DIO, clock div:2 - load:0x3fff0018,len:4 - load:0x3fff001c,len:10464 - ho 0 tail 12 room 4 - load:0x40078000,len:19168 - load:0x40080400,len:6664 - entry 0x40080764 - I (60) boot: ESP-IDF v4.0-dev-763-g2c55fae6c-dirty 2nd stage bootloader - I (60) boot: compile time 19:15:54 - I (62) boot: Enabling RNG early entropy source... - I (67) boot: SPI Speed : 40MHz - I (72) boot: SPI Mode : DIO - I (76) boot: SPI Flash Size : 4MB - E (80) flash_parts: partition 0 invalid magic number 0x94f6 - E (86) boot: Failed to verify partition table - E (91) boot: load partition table error! - -3. 如果引导加载程序和分区表已加密,但使用明文应用程序映像重新更新了应用程序,则引导加载程序将无法加载新的应用程序,并会显示以下错误类型: - - :: - - rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) - configsip: 0, SPIWP:0xee - clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 - mode:DIO, clock div:2 - load:0x3fff0018,len:4 - load:0x3fff001c,len:8452 - load:0x40078000,len:13616 - load:0x40080400,len:6664 - entry 0x40080764 - I (56) boot: ESP-IDF v4.0-dev-850-gc4447462d-dirty 2nd stage bootloader - I (56) boot: compile time 15:37:14 - I (58) boot: Enabling RNG early entropy source... - I (64) boot: SPI Speed : 40MHz - I (68) boot: SPI Mode : DIO - I (72) boot: SPI Flash Size : 4MB - I (76) boot: Partition Table: - I (79) boot: ## Label Usage Type ST Offset Length - I (87) boot: 0 nvs WiFi data 01 02 0000a000 00006000 - I (94) boot: 1 phy_init RF data 01 01 00010000 00001000 - I (102) boot: 2 factory factory app 00 00 00020000 00100000 - I (109) boot: End of partition table - E (113) esp_image: image at 0x20000 has invalid magic byte - W (120) esp_image: image at 0x20000 has invalid SPI mode 108 - W (126) esp_image: image at 0x20000 has invalid SPI size 11 - E (132) boot: Factory app partition is not bootable - E (138) boot: No bootable app partitions in the partition table - -Flash 加密的要点 -------------------- - -- 使用 AES-256 加密 Flash 的内容。Flash 加密密钥存储于 eFuse 内部的芯片中,并(默认)受保护防止软件访问。 - -- `flash 加密算法` 采用的是 AES-256,其中密钥随着 Flash 的每个 32 字节块的偏移地址“调整”。这意味着,每个 32 字节块(2 个连续的 16 字节 AES 块)使用从 Flash 加密密钥中产生的一个特殊密钥进行加密。 - -- 通过 {IDF_TARGET_NAME} 的 Flash 缓存映射功能,Flash 可支持透明访问——读取任何映射到地址空间的 Flash 区域时,都将透明解密该区域。 - - 为便于访问,某些数据分区最好保持未加密状态,或者也可使用对已加密数据无效的 Flash 友好型更新算法。由于 NVS 库无法与 Flash 加密直接兼容,因此无法加密非易失性存储器的 NVS 分区。详情可参见 :ref:`NVS 加密 `。 - -- 如果可能已启用 Flash 加密,则编写 :ref:`使用加密 flash ` 的代码时,编程人员须小心谨慎。 - -- 如果已启用安全启动,则重新烧录加密设备的引导加载程序则需要“可重新烧录”的安全启动摘要(可参见 :ref:`flash-encryption-and-secure-boot`)。 +1. 如果通过 **明文固件引导加载程序镜像** 重新烧录了引导加载程序分区,则 ROM 加载器将无法加载固件引导加载程序,并会显示以下错误类型: .. only:: esp32 - .. note:: 同时启用安全启动和 Flash 加密后,引导加载程序 app 二进制文件 ``bootloader.bin`` 可能会过大。参见 :ref:`bootloader-size`。 + .. code-block:: bash -.. important:: - 在首次启动加密过程中,请勿中断 {IDF_TARGET_NAME} 的电源。如果电源中断,Flash 的内容将受到破坏,并需要重新烧录未加密数据。而这类重新烧录将不计入烧录限制次数。 + rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) + flash read err, 1000 + ets_main.c 371 + ets Jun 8 2016 00:22:57 -.. _using-encrypted-flash: + rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) + flash read err, 1000 + ets_main.c 371 + ets Jun 8 2016 00:22:57 -使用加密的 Flash -------------------- + rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) + flash read err, 1000 + ets_main.c 371 + ets Jun 8 2016 00:22:57 -{IDF_TARGET_NAME} app 代码可通过调用函数 :cpp:func:`esp_flash_encryption_enabled` 来确认当前是否已启用 Flash 加密。同时,设备可通过调用函数 :cpp:func:`esp_get_flash_encryption_mode` 来识别使用的 Flash 加密模式。 + rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) + flash read err, 1000 + ets_main.c 371 + ets Jun 8 2016 00:22:57 -启用 Flash 加密后,使用代码访问 Flash 内容时需加注意。 + rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) + flash read err, 1000 + ets_main.c 371 + ets Jun 8 2016 00:22:57 -Flash 加密的范围 -^^^^^^^^^^^^^^^^^^ +.. only:: not esp32 -只要 ``FLASH_CRYPT_CNT`` eFuse 设置为奇数位的值,所有通过 MMU 的 Flash 缓存访问的 Flash 内容都将被透明解密。包括: + .. code-block:: bash + + rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) + invalid header: 0xb414f76b + invalid header: 0xb414f76b + invalid header: 0xb414f76b + invalid header: 0xb414f76b + invalid header: 0xb414f76b + invalid header: 0xb414f76b + invalid header: 0xb414f76b + + .. note:: + + 不同应用程序中无效头文件的值不同。 + +.. note:: + + 如果 flash 内容被擦除或损坏,也会出现这个错误。 + +2. 如果固件的引导加载程序已加密,但通过 **明文分区表镜像** 重新烧录了分区表,引导加载程序将无法读取分区表,从而出现以下错误: + + .. code-block:: bash + + rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) + configsip: 0, SPIWP:0xee + clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 + mode:DIO, clock div:2 + load:0x3fff0018,len:4 + load:0x3fff001c,len:10464 + ho 0 tail 12 room 4 + load:0x40078000,len:19168 + load:0x40080400,len:6664 + entry 0x40080764 + I (60) boot: ESP-IDF v4.0-dev-763-g2c55fae6c-dirty 2nd stage bootloader + I (60) boot: compile time 19:15:54 + I (62) boot: Enabling RNG early entropy source... + I (67) boot: SPI Speed : 40MHz + I (72) boot: SPI Mode : DIO + I (76) boot: SPI Flash Size : 4MB + E (80) flash_parts: partition 0 invalid magic number 0x94f6 + E (86) boot: Failed to verify partition table + E (91) boot: load partition table error! + +3. 如果引导加载程序和分区表已加密,但使用 **明文应用程序镜像** 重新烧录了应用程序,引导加载程序将无法加载应用程序,从而出现以下错误: + + .. code-block:: bash + + rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) + configsip: 0, SPIWP:0xee + clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 + mode:DIO, clock div:2 + load:0x3fff0018,len:4 + load:0x3fff001c,len:8452 + load:0x40078000,len:13616 + load:0x40080400,len:6664 + entry 0x40080764 + I (56) boot: ESP-IDF v4.0-dev-850-gc4447462d-dirty 2nd stage bootloader + I (56) boot: compile time 15:37:14 + I (58) boot: Enabling RNG early entropy source... + I (64) boot: SPI Speed : 40MHz + I (68) boot: SPI Mode : DIO + I (72) boot: SPI Flash Size : 4MB + I (76) boot: Partition Table: + I (79) boot: ## Label Usage Type ST Offset Length + I (87) boot: 0 nvs WiFi data 01 02 0000a000 00006000 + I (94) boot: 1 phy_init RF data 01 01 00010000 00001000 + I (102) boot: 2 factory factory app 00 00 00020000 00100000 + I (109) boot: End of partition table + E (113) esp_image: image at 0x20000 has invalid magic byte + W (120) esp_image: image at 0x20000 has invalid SPI mode 108 + W (126) esp_image: image at 0x20000 has invalid SPI size 11 + E (132) boot: Factory app partition is not bootable + E (138) boot: No bootable app partitions in the partition table + + +.. _flash-encryption-status: + +{IDF_TARGET_NAME} flash 加密状态 +----------------------------------------- + +1. 确保您的 {IDF_TARGET_NAME} 设备有 :ref:`flash-encryption-efuse` 中所示的 flash 加密 eFuse 的默认设置。 + +要检查您的 {IDF_TARGET_NAME} 设备上是否启用了 flash 加密,请执行以下操作之一: + +- 将应用示例 :example:`security/flash_encryption` 烧录到您的设备上。此应用程序会打印 ``{IDF_TARGET_CRYPT_CNT}`` eFuse 值,以及是否启用了 flash 加密。 + +- :doc:`查询设备所连接的串口名称 <../get-started/establish-serial-connection>`,在以下命令中将 ``PORT`` 替换为串口名称后运行: + + .. code-block:: bash + + espefuse.py -p PORT summary + + +.. _reading-writing-content: + +在加密的 flash 中读写数据 +------------------------------------- + +{IDF_TARGET_NAME} 应用程序代码可以通过调用函数 :cpp:func:`esp_flash_encryption_enabled` 来检查当前是否启用了 flash 加密。此外,设备可以通过调用函数 :cpp:func:`esp_get_flash_encryption_mode` 来识别 flash 加密模式。 + +一旦启用 flash 加密,使用代码访问 flash 内容时要更加小心。 + + +Flash 加密范围 +^^^^^^^^^^^^^^^^^^^^^^^^^ + +当 ``{IDF_TARGET_CRYPT_CNT}`` eFuse 设置为奇数位的值,所有通过 MMU 的 flash 缓存访问的 flash 内容都将被透明解密。包括: - Flash 中可执行的应用程序代码 (IROM)。 -- 所有存储于 Flash 中的只读数据 (DROM)。 +- 所有存储于 flash 中的只读数据 (DROM)。 - 通过函数 :cpp:func:`spi_flash_mmap` 访问的任意数据。 -- ROM 引导加载程序读取的软件引导加载程序映像。 +- ROM 引导加载程序读取的固件引导加载程序镜像。 .. important:: - MMU Flash 缓存将无条件解密所有数据。Flash 中未加密存储的数据将通过 Flash 缓存“被透明解密”,并在软件中存储为随机垃圾数据。 -读取加密的 Flash + MMU flash 缓存将无条件解密所有数据。Flash 中未加密存储的数据将通过 flash 缓存“被透明解密”,并在软件中存储为随机垃圾数据。 + + +读取加密的 flash ^^^^^^^^^^^^^^^^^^ -如在不使用 Flash 缓存 MMU 映射的情况下读取数据,推荐使用分区读取函数 :cpp:func:`esp_partition_read`。使用该函数时,只有从加密分区读取的数据才会被解密。其他分区的数据将以未加密形式读取。这样,软件便能同样访问加密和未加密的 Flash。 +如果需要在不使用 flash 缓存 MMU 映射的情况下读取数据,推荐使用分区读取函数 :cpp:func:`esp_partition_read`。该函数只会解密从加密分区读取的数据。从未加密分区读取的数据不会被解密。这样,软件便能以相同的方式访问加密和未加密的 flash。 -通过其他 SPI 读取 API 读取的数据均未解密: +也可以使用以下 SPI flash API 函数: -- 通过函数 :cpp:func:`spi_flash_read` 读取的数据均未解密。 -- 通过 ROM 函数 :cpp:func:`SPIRead` 读取的数据均未解密(esp-idf app 不支持该函数)。 -- 使用非易失性存储器 (NVS) API 存储的数据始终从 Flash 加密的角度进行存储和读取解密。如有需要,则由库提供加密功能。详情可参见 :ref:`NVS 加密 `。 +- 通过函数 :cpp:func:`esp_flash_read` 读取不会被解密的原(加密)数据。 +- 通过函数 :cpp:func:`esp_flash_read_encrypted` 读取和解密数据。 -写入加密的 Flash +ROM 函数 :cpp:func:`SPIRead` 可以在不解密的情况下读取数据,但是在 esp-idf 应用程序中不支持该函数。 + +使用非易失性存储器 (NVS) API 存储的数据始终从 flash 加密的角度进行存储和读取解密。如有需要,则由库提供加密功能。详情可参考 :ref:`NVS 加密 `。 + + +写入加密的 flash ^^^^^^^^^^^^^^^^^^ -在可能的情况下,推荐使用分区写入函数 ``esp_partition_write``。使用该函数时,只有向加密分区写入的数据才会被加密。而写入其他分区的数据均未加密。这样,软件便可同样访问加密和未加密的 Flash。 +推荐使用分区写入函数 :cpp:func:`esp_partition_write`。此函数只会在将数据写入加密分区时加密数据,而写入未加密分区的数据不会被加密。通过这种方式,软件可以以相同的方式访问加密和非加密 flash。 -当 write_encrypted 参数设置为“是”时,函数 ``esp_spi_flash_write`` 将写入数据。否则,数据将以未加密形式写入。 +也可以使用函数 :cpp:func:`esp_flash_write_encrypted` 预加密和写入数据。 -ROM 函数 ``esp_rom_spiflash_write_encrypted`` 将在 Flash 中写入加密数据,而 ROM 函数 ``SPIWrite`` 将在 Flash 中写入未加密数据(esp-idf app 不支持上述函数)。 +此外,esp-idf 应用程序中存在但不支持以下 ROM 函数: + +- ``esp_rom_spiflash_write_encrypted`` 预加密并将数据写入 flash +- ``SPIWrite`` 将未加密的数据写入 flash + +由于数据是按块加密的,加密数据最小的写入大小为 16 字节,对齐也是 16 字节。 -由于数据均采用块加密方式,加密数据最小的写入大小为 16 字节(16字节对齐)。 .. _updating-encrypted-flash: -更新加密的 Flash +更新加密的 flash ------------------- .. _updating-encrypted-flash-ota: @@ -536,83 +651,122 @@ ROM 函数 ``esp_rom_spiflash_write_encrypted`` 将在 Flash 中写入加密数 OTA 更新 ^^^^^^^^^^ -只要使用了函数 ``esp_partition_write``,则加密分区的 OTA 更新将自动以加密形式写入。 +如果使用函数 :cpp:func:`esp_partition_write`,对加密分区的 OTA 更新将自动以加密形式写入。 + +在为已加密设备的 OTA 更新构建应用程序镜像之前,启用项目配置菜单中的 :ref:`启动时使能 flash 加密 ` 选项。 + +请参考 :doc:`OTA <../api-reference/system/ota>` 获取更多关于 ESP-IDF OTA 更新的信息。 + .. _updating-encrypted-flash-serial: -关闭 Flash 加密 +通过串口更新加密 flash +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +通过串行引导加载程序烧录加密设备,需要串行引导加载程序下载接口没有通过 eFuse 被永久禁用。 + +在开发模式下,推荐的方法是 :ref:`encrypt-partitions`。 + +在发布模式下,如果主机上有存储在 eFuse 中的相同密钥的副本,那么就可以在主机上对文件进行预加密,然后进行烧录,具体请参考 :ref:`manual-encryption`。 + +关闭 flash 加密 ----------------- -若因某些原因意外启用了 Flash 加密,则接下来烧录明文数据时将使 {IDF_TARGET_NAME} 软砖(设备不断重启,并报错 ``flash read err, 1000``)。 - -可通过写入 ``FLASH_CRYPT_CNT`` eFuse 再次关闭 Flash 加密(仅适用于开发模式下): - -- 首先,前往 :ref:`project-configuration-menu`,在“安全性能”目录下关闭 :ref:`启用 Flash 加密启动 `。 -- 退出 menuconfig 并保存最新配置。 -- 再次运行 ``idf.py menuconfig`` 并复核是否确认已关闭该选项!*如果该选项仍处于已启用状态,则引导加载程序会在启动后立即重新启用加密*。 -- 在未启用 Flash 加密的状态下,运行 ``idf.py flash`` 构建并烧录新的引导加载程序与 app。 -- 运行 ``espefuse.py`` (``components/esptool_py/esptool`` 中)以关闭 FLASH_CRYPT_CNT:: - - espefuse.py burn_efuse FLASH_CRYPT_CNT - -重置 {IDF_TARGET_NAME},Flash 加密应处于关闭状态,引导加载程序将正常启动。 - -.. _flash-encryption-limitations: - -Flash 加密的局限性 --------------------- - -Flash 加密可防止从加密 Flash 中读取明文,从而保护固件防止未经授权的读取与修改。了解 Flash 加密系统的局限之处亦十分重要: - -- Flash 加密功能与密钥同样稳固。因而,推荐您首次启动设备时在设备上生成密钥(默认行为)。如果在设备外生成密钥,请确保遵循正确的后续步骤。 - -- 并非所有数据都是加密存储。因而在 Flash 上存储数据时,请检查您使用的存储方式(库、API等)是否支持 Flash 加密。 - -- Flash 加密无法防止攻击者获取 Flash 的高层次布局信息。这是因为同一个 AES 密钥要用于每对相邻的 16 字节 AES 块。当这些相邻的 16 字节块中包含相同内容时(如空白或填充区域),这些字节块将加密以产生匹配的加密块对。这可能使得攻击者可在加密设备间进行高层次对比(例如,确认两设备是否可能在运行相同的固件版本)。 - -- 出于相同原因,攻击者始终可获知一对相邻的 16 字节块(32 字节对齐)何时包含相同内容。因此,在 Flash 上存储敏感数据时应牢记这点,并进行相关设置避免该情况发生(可使用计数器字节或每 16 字节设置不同的值即可)。 +如果意外启用了 flash 加密,则明文数据的 flash 会使 {IDF_TARGET_NAME} 无法正常启动。设备将不断重启,并报错 ``flash read err, 1000`` 或 ``invalid header: 0xXXXXXX``。 .. only:: esp32 - - 单独使用 Flash 加密可能无法防止攻击者修改本设备的固件。为防止设备上运行未经授权的固件,可搭配 Flash 加密使用 :doc:`安全启动 `。 + 对于开发模式下的 flash 加密,可以通过烧录 ``{IDF_TARGET_CRYPT_CNT}`` efuse 来关闭加密。每个芯片仅有 3 次机会,请执行以下步骤: + +.. only:: not esp32 + + 对于开发模式下的 flash 加密,可以通过烧录 ``{IDF_TARGET_CRYPT_CNT}`` efuse 来关闭加密。每个芯片仅有 1 次机会,请执行以下步骤: + +#. 在 :ref:`项目配置菜单 ` 中,禁用 :ref:`启动时使能 flash 加密 ` 选项,然后保存并退出。 +#. 再次打开项目配置菜单,再次检查你是否已经禁用了该选项,如果这个选项仍被启用,引导加载程序在启动时将立即重新启用加密功能。 +#. 在禁用 flash 加密后,通过运行 ``idf.py flash`` 来构建和烧录新的引导加载程序和应用程序。 +#. 使用 ``espefuse.py`` (在 ``components/esptool_py/esptool`` 中)以关闭 ``{IDF_TARGET_CRYPT_CNT}``,运行: + + .. code-block:: bash + + espefuse.py burn_efuse {IDF_TARGET_CRYPT_CNT} + +重置 {IDF_TARGET_NAME},flash 加密应处于关闭状态,引导加载程序将正常启动。 + + +Flash 加密的要点 +--------------------------------- + +.. list:: + + :esp32: - 使用 AES-256 加密 flash。Flash 加密密钥存储于芯片内部的 ``flash_encryption`` eFuse 中,并(默认)受保护,防止软件访问。 + + :esp32: - Flash 加密算法采用的是 AES-256,其中密钥随着 flash 的每个 32 字节块的偏移地址“调整”。这意味着,每个 32 字节块(2 个连续的 16 字节 AES 块)使用从 flash 加密密钥中产生的一个特殊密钥进行加密。 + + :esp32s2 or esp32s3: - 使用 XTS-AES-128 或 XTS-AES-256 加密 flash。Flash 加密密钥分别为 256 位和 512 位,存储于芯片内部一个或两个 ``BLOCK_KEYN`` eFuse 中,并(默认)受保护,防止软件访问。 + + :esp32c3: - 使用 XTS-AES-128 加密 flash。 Flash 加密密钥为 256 位,存储于芯片内部的 ``BLOCK_KEYN`` eFuse 中,并(默认)受保护,防止软件访问。 + + - 通过 {IDF_TARGET_NAME} 的 flash 缓存映射功能,flash 可支持透明访问——任何映射到地址空间的 flash 区域在读取时都将被透明地解密。 + + 为便于访问,某些数据分区最好保持未加密状态,或者也可使用对已加密数据无效的 flash 友好型更新算法。由于 NVS 库无法与 flash 加密直接兼容,因此无法加密非易失性存储器的 NVS 分区。详情可参见 :ref:`NVS 加密 `。 + + - 如果以后可能需要启用 flash 加密,则编程人员在编写 :ref:`使用加密 flash ` 代码时需小心谨慎。 + + - 如果已启用安全启动,重新烧录加密设备的引导加载程序则需要“可重新烧录”的安全启动摘要(可参考 :ref:`flash-encryption-and-secure-boot`)。 + + 启用 flash 加密将增大引导加载程序,因此可能需更新分区表偏移量。请参考 :ref:`引导加载程序大小 `。 + + .. important:: + + 在首次启动加密过程中,请勿切断 {IDF_TARGET_NAME} 的电源。如果电源被切断,flash 的内容将受到破坏,并需要重新烧录未加密数据。而这类重新烧录将不计入烧录限制次数。 + + +.. _flash-encryption-limitations: + +Flash 加密的局限性 +-------------------- + +flash 加密可以保护固件,防止未经授权的读取与修改。了解 flash 加密系统的局限之处亦十分重要: + +.. list:: + + - Flash 加密功能与密钥同样稳固。因而,推荐您首次启动设备时在设备上生成密钥(默认行为)。如果在设备外生成密钥,请确保遵循正确的后续步骤,不要在所有生产设备之间使用相同的密钥。 + - 并非所有数据都是加密存储。因而在 flash 上存储数据时,请检查您使用的存储方式(库、API 等)是否支持 flash 加密。 + - Flash 加密无法防止攻击者获取 flash 的高层次布局信息。这是因为每对相邻的 16 字节 AES 块都使用相邻的 AES 密钥。当这些相邻的 16 字节块中包含相同内容时(如空白或填充区域),这些字节块将加密以产生匹配的加密块对。这让攻击者可在加密设备间进行高层次对比(例如,确认两设备是否可能运行相同的固件版本)。 + :esp32: - 出于相同原因,攻击者始终可获知一对相邻的 16 字节块(32 字节对齐)何时包含相同的 16 字节序列。因此,在 flash 上存储敏感数据时应牢记这点,可进行相关设置避免该情况发生(可使用计数器字节或每 16 字节设置不同的值即可)。具体请参考 :ref:`NVS 加密 `。 + - 单独使用 flash 加密可能无法防止攻击者修改本设备的固件。为防止设备上运行未经授权的固件,可搭配 flash 加密使用 :doc:`安全启动 `。 .. _flash-encryption-and-secure-boot: Flash 加密与安全启动 ---------------------- -推荐搭配使用 Flash 加密与安全启动。但是,如果已启用安全启动,则重新烧录设备时会受到其他限制: +推荐 flash 加密与安全启动搭配使用。但是,如果已启用安全启动,则重新烧录设备时会受到其他限制: -- :ref:`updating-encrypted-flash-ota` 不受限制(如果新的 app 已使用安全启动签名密钥进行正确签名)。 +- 如果新的应用程序已使用安全启动签名密钥正确签名,则 :ref:`updating-encrypted-flash-ota` 不受限制。 .. only:: esp32 - - 只有当选择 :ref:`可再次烧录 ` 安全启动模式,且安全启动密钥已预生成并烧录至 {IDF_TARGET_NAME}(可参见 :ref:`安全启动 `),则 :ref:`明文串行 flash 更新 ` 可实现。在该配置下,``idf.py bootloader`` 将生成简化的引导加载程序和安全启动摘要文件,用于在偏移量 0x0 处进行烧录。当进行明文串行重新烧录步骤时,须在烧录其他明文数据前重新烧录此文件。 + - 只有当选择 :ref:`可再次烧录 ` 安全启动模式,且安全启动密钥已预生成并烧录至 {IDF_TARGET_NAME} 时(可参见 :ref:`安全启动 `),:ref:`明文串行 flash 更新 ` 才可能实现。在该配置下,``idf.py bootloader`` 将生成简化的引导加载程序和安全启动摘要文件,在偏移量 0x0 处进行烧录。当进行明文串行重新烧录步骤时,需在烧录其他明文数据前重新烧录此文件。 - - 假设未重新烧录引导加载程序,:ref:`使用预生成的 Flash 加密密钥重新烧录 ` 仍可实现。重新烧录引导加载程序时,需在安全启动配置中启用相同的 :ref:`可重新烧录 ` 选项。 + - 如果未重新烧录引导加载程序,则仍然可以 :ref:`使用预生成的 flash 加密密钥重新烧录 `。重新烧录引导加载程序时,需在安全启动配置中启用相同的 :ref:`可重新烧录 ` 选项。 .. _flash-encryption-without-secure-boot: -使用无安全启动的 Flash 加密 ------------------------------ - -尽管 Flash 加密与安全启动可独立使用,但强烈建议您将这二者 **搭配使用** 以确保更高的安全性。 - -.. _flash-encryption-advanced-features: - Flash 加密的高级功能 ----------------------- +-------------------------------- -以下信息可帮助您使用 Flash 加密的高级功能: +以下部分介绍了 flash 加密的高级功能。 + +.. _encrypted-partition-flag: 加密分区标志 ^^^^^^^^^^^^^ -部分分区默认为已加密。除此之外,可将任意分区标记为需加密: +部分分区默认为已加密。通过在分区的标志字段中添加 “encrypted” 标志,可在分区表描述中将其他分区标记为需要加密。在这些标记分区中的数据会和应用程序分区一样视为加密数据。 -在 :doc:`分区表 <../api-guides/partition-tables>` 文档对 CSV 文件的描述中有标志字段。 - -该字段通常保留为空白。如果在字段中写入"encrypted",则这个分区将在分区表中标记为已加密,此处写入的数据也视为加密数据(app 分区同样适用):: +.. code-block:: bash # Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x6000 @@ -620,87 +774,214 @@ Flash 加密的高级功能 factory, app, factory, 0x10000, 1M secret_data, 0x40, 0x01, 0x20000, 256K, encrypted +请参考 :doc:`分区表 <../api-guides/partition-tables>` 获取更多关于分区表描述的具体信息。 + +关于分区加密您还需要了解以下信息: + - 默认分区表都不包含任何加密数据分区。 +- 启用 flash 加密后,"app" 分区一般都视为加密分区,因此无需标记。 +- 如果未启用 flash 加密,则 "encrypted" 标记无效。 +- 将可选 ``phy`` 分区标记为 "encrypted",可以防止物理访问读取或修改 ``phy_init`` 数据。 +- ``nvs`` 分区无法标记为 "encrypted" 因为 NVS 库与 flash 加密不直接兼容。 -- "app"分区一般都视为加密分区,因此无需将其标记为已加密。 - -- 如果未启用 Flash 加密,则"encrypted"标记无效。 - -- 可将带有 ``phy_init`` 数据的可选 ``phy`` 分区标记为已加密,保护该数据防止物理访问读取或修改。 - -- ``nvs`` 分区无法标记为已加密。 .. _uart-bootloader-encryption: 启用 UART 引导加载程序加密/解密 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -默认情况下,首次启动 Flash 加密过程中将烧录 eFuse ``DISABLE_DL_ENCRYPT``、``DISABLE_DL_DECRYPT`` 和 ``DISABLE_DL_CACHE``: +在第一次启动时,flash 加密过程默认会烧录以下 eFuse: -- ``DISABLE_DL_ENCRYPT`` 在 UART 引导加载程序启动模式下运行时,终止 Flash 加密操作。 -- ``DISABLE_DL_DECRYPT`` 在 UART 引导加载程序模式下运行时,终止透明 Flash 解密(即使 FLASH_CRYPT_CNT 已设置为在正常操作中启用 Flash 透明解密)。 -- ``DISABLE_DL_CACHE`` 在 UART 引导加载程序模式下运行时终止整个 MMU flash 缓存。 +.. only:: esp32 -为了完整保存数据,可在首次启动前仅烧录部分 eFuse,并写保护其他部分(未设置值为 0)。例如:: + - ``DISABLE_DL_ENCRYPT`` 在 UART 引导加载程序启动模式下运行时,禁止 flash 加密操作。 + - ``DISABLE_DL_DECRYPT`` 在 UART 引导加载程序模式下运行时,禁止透明 flash 解密(即使 eFuse ``{IDF_TARGET_CRYPT_CNT}`` 已设置为在正常操作中启用 flash 透明解密)。 + - ``DISABLE_DL_CACHE`` 在 UART 引导加载程序模式下运行时禁止整个 MMU flash 缓存。 - espefuse.py --port PORT burn_efuse DISABLE_DL_DECRYPT - espefuse.py --port PORT write_protect_efuse DISABLE_DL_ENCRYPT +.. only:: not esp32 -(请注意,一个写保护位即可关闭这 3 个 eFuse,因此,写保护一个 eFuse 将写保护上述所有 eFuse。所以,在写保护前须设置任意位)。 + .. list:: -.. important:: - 由于 ``esptool.py`` 不支持读取加密的 Flash,因此目前基本无法通过写保护这些 eFuse 来将其保持为未设状态。 + - ``DIS_DOWNLOAD_MANUAL_ENCRYPT`` 在 UART 引导加载程序启动模式下运行时,禁止 flash 加密操作。 + :esp32s2 or esp32s3: - ``DIS_DOWNLOAD_ICACHE`` 和 ``DIS_DOWNLOAD_DCACHE`` 在 UART 引导加载程序模式下运行时禁止整个 MMU flash 缓存。 + :esp32c3: - ``DIS_DOWNLOAD_ICACHE`` 在 UART 引导加载程序模式下运行时禁止整个 MMU flash 缓存。 + :esp32s2: - ``HARD_DIS_JTAG`` 禁用 JTAG。 + :esp32c3: - ``DIS_PAD_JTAG`` 和 ``DIS_USB_JTAG`` 禁用 JTAG。 + :esp32s3: - ``HARD_DIS_JTAG`` 和 ``DIS_USB_JTAG`` 禁用 JTAG。 + - ``DIS_LEGACY_SPI_BOOT`` 禁用传统的 SPI 启动模式。 -.. important:: - 如果保留 ``DISABLE_DL_DECRYPT`` 未设置(为 0),则实际上将使 Flash 加密无效,因为此时有物理访问权限的攻击者便可使用 UART 引导加载程序模式(使用自定义存根代码)读取 Flash 的内容。 +为了能启用这些功能,可在首次启动前仅烧录部分 eFuse,并用未设置值 0 写保护其他部分。例如: -.. _setting-flash-crypt-config: +.. only:: esp32 -设置 FLASH_CRYPT_CONFIG -^^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. code-block:: bash -``FLASH_CRYPT_CONFIG`` eFuse 决定 Flash 加密密钥中随块偏移“调整”的位数。详情可参见 :ref:`flash-encryption-algorithm`。 + espefuse.py --port PORT burn_efuse DISABLE_DL_DECRYPT + espefuse.py --port PORT write_protect_efuse DISABLE_DL_ENCRYPT -首次启动 引导加载程序时,该值始终设置为最大 `0xF`。 +.. only:: not esp32 -可手动写入这些 eFuse,并在首次启动前对其写保护,以便选择不同的调整值。但不推荐该操作。 + .. code-block:: bash + + espefuse.py --port PORT burn_efuse DIS_DOWNLOAD_MANUAL_ENCRYPT + espefuse.py --port PORT write_protect_efuse DIS_DOWNLOAD_MANUAL_ENCRYPT + + .. note:: + + 请注意在写保护前设置所有适当的位! + + 一个位可以控制三个 eFuse 的写保护,这意味着写保护一个 eFuse 位将写保护所有未设置的 eFuse 位。 + + 由于 ``esptool.py`` 目前不支持读取加密 flash,所以对这些 eFuse 进行写保护从而使其保持未设置目前来说并不是很有用。 + +.. only:: esp32 + + .. important:: + + 保持 ``DISABLE_DL_DECRYPT`` 未设置 (0) 会使 flash 加密无效。 + + 对芯片具有物理访问权限的攻击者会使用 UART 引导加载程序模式(使用自定义存根代码)读取 flash 的内容。 + + +.. only:: esp32 + + .. _setting-flash-crypt-config: + + 设置 FLASH_CRYPT_CONFIG + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + ``FLASH_CRYPT_CONFIG`` eFuse 决定 flash 加密密钥中随块偏移“调整”的位数。详情可参考 :ref:`flash-encryption-algorithm`。 + + 首次启动固件引导加载程序时,该值始终设置为最大值 `0xF`。 + + 可手动写入这些 eFuse,并在首次启动前对其写保护,以便选择不同的调整值。但不推荐该操作。 + + 强烈建议在 ``FLASH_CRYPT_CONFIG`` 未设置时,不要对其进行写保护。否则,它的值将永久为零,而 flash 加密密钥中则无调整位。这导致 flash 加密算法等同于 AES ECB 模式。 + +JTAG 调试 +^^^^^^^^^^^^^^ + +默认情况下,当启用 flash 加密(开发或发布模式)时,将通过 eFuse 禁用 JTAG 调试。引导加载程序在首次启动时执行此操作,同时启用 flash 加密。 + +请参考 :ref:`jtag-debugging-security-features` 了解更多关于使用 JTAG 调试与 flash 加密的信息。 + + +.. _manual-encryption: + +手动加密文件 +^^^^^^^^^^^^^^^^^^^^ + +手动加密或解密文件需要在 eFuse 中预烧录 flash 加密密钥(请参阅 :ref:`pregenerated-flash-encryption-key`)并在主机上保留一份副本。 如果 flash 加密配置在开发模式下,那么则不需要保留密钥的副本或遵循这些步骤,可以使用更简单的 :ref:`encrypt-partitions` 步骤。 + +密钥文件应该是单个原始二进制文件(例如:``key.bin``)。 + +.. only:: SOC_FLASH_ENCRYPTION_XTS_AES_256 + + .. note:: + + 如果使用 AES-XTS-256,那么密钥文件将被生成两部分(``XTS_AES_256_KEY_1`` 和 ``XTS_AES_256_KEY_2``),可通过 ``espefuse.py`` 进行编程。 ``espsecure.py`` 目前仅支持用于加密/解密的单个密钥文件,因此用于 ``XTS_AES_256_KEY_1`` 和 ``XTS_AES_256_KEY_2`` 的各个文件应手动合并以创建一个 64 字节长的单个文件。 + +例如,以下是将文件 ``build/my-app.bin`` 进行加密、烧录到偏移量 0x10000 的步骤。运行 ``espsecure.py``,如下所示: + +.. only:: esp32 + + .. code-block:: bash + + espsecure.py encrypt_flash_data --keyfile /path/to/key.bin --address 0x10000 --output my-app-ciphertext.bin build/my-app.bin + +.. only:: not esp32 + + .. code-block:: bash + + espsecure.py encrypt_flash_data --aes_xts --keyfile /path/to/key.bin --address 0x10000 --output my-app-ciphertext.bin build/my-app.bin + +然后可以使用 ``esptool.py`` 将文件 ``my-app-ciphertext.bin`` 写入偏移量 0x10000。 关于为 ``esptool.py`` 推荐的所有命令行选项,请查看 idf.py build 成功时打印的输出。 + +.. note:: + + 如果 {IDF_TARGET_NAME} 在启动时无法识别烧录进去的密文文件,请检查密钥是否匹配以及命令行参数是否完全匹配,包括偏移量是否正确。 + + .. only:: esp32 + + 如果您的 ESP32 在 eFuse 中使用了非默认的 :ref:`FLASH_CRYPT_CONFIG 值 `,那么则需要向 ``espsecure.py`` 传递 ``--flash_crypt_conf`` 参数以设置匹配的值。如果设备自己设置了 flash 加密就不会出现这种情况,但如果手动烧录 eFuse 来启用 flash 加密就可能发生这种情况。 + +``espsecure.py decrypt_flash_data`` 命令可以使用同样的选项(和不同的输入/输出文件)来解密 flash 密文或之前加密的文件。 + + +.. only:: SOC_SPIRAM_SUPPORTED and not esp32 + + 片外 RAM + ------------ + + 启用 flash 加密后,任何通过缓存从片外 SPI RAM 读取和写入的数据也将被加密/解密。这个实现的方式以及使用的密钥与 flash 加密相同。如果启用 flash 加密,则片外 SPI RAM 的加密也会被启用,无法单独控制此功能。 -当 ``FLASH_CRYPT_CONFIG`` 的值为 0 时,强烈建议始终不对其进行写保护。如果该 eFuse 设置为 0,则 Flash 加密密钥中无调整位,且 Flash 加密算法相当于 AES ECB 模式。 技术细节 ----------- -下节将提供 Flash 加密操作的相关信息。 +以下章节将提供 flash 加密操作的相关信息。 -.. _flash-encryption-algorithm: +.. only:: not SOC_FLASH_ENCRYPTION_XTS_AES -Flash 加密算法 -^^^^^^^^^^^^^^^^ + .. _flash-encryption-algorithm: -- AES-256 在 16 字节的数据块上运行。Flash 加密引擎在 32 字节的数据块和 2 个 串行 AES 块上加密或解密数据。 + Flash 加密算法 + ^^^^^^^^^^^^^^^^ -- Flash 加密的主密钥存储于 eFuse (BLOCK1) 中,默认受保护防止进一步写入或软件读取。 + - AES-256 在 16 字节的数据块上运行。Flash 加密引擎在 32 字节的数据(2 个 串行 AES 块)上加密或解密数据。 -- AES-256 密钥大小为 256 位(32 字节),从 eFuse block 1 中读取。硬件 AES 引擎使用反字节序密钥于 eFuse 块中存储的字节序。 + - Flash 加密的主密钥存储于 ``flash_encryption`` eFuse 中,默认受保护防止进一步写入或软件读取。 - - 如果 ``CODING_SCHEME`` eFuse 设置为 0(默认“无”编码方案),则 eFuse 密钥块为 256 位,且密钥按原方式存储(反字节序)。 - - 如果 ``CODING_SCHEME`` eFuse 设置为 1(3/4 编码),则 eFuse 密钥块为 192 位(反字节序),信息熵总量减少。硬件 Flash 加密仍在 256 字节密钥上运行,在读取后(字节序未反向),密钥扩展为 ``key = key[0:255] + key[64:127]``。 + - AES-256 密钥大小为 256 位(32 字节),从 ``flash_encryption`` eFuse 中读取。与 ``flash_encryption`` 中的存储顺序相比,硬件 AES 引擎使用的是相反的字节顺序的密钥。 -- Flash 加密中使用了逆向 AES 算法,因此 Flash 加密的“加密”操作相当于 AES 解密,而其“解密”操作则相当于 AES 加密。这是为了优化性能,不会影响算法的有效性。 + - 如果 ``CODING_SCHEME`` eFuse 设置为 0(默认“无”编码方案),则 eFuse 密钥块为 256 位,且密钥按原方式存储(反字节序)。 + - 如果 ``CODING_SCHEME`` eFuse 设置为 1(3/4 编码),则 eFuse 密钥块为 192 位(反字节序),信息熵总量减少。硬件 flash 加密仍在 256 字节密钥上运行,在读取后(字节序未反向),密钥扩展为 ``key = key[0:255] + key[64:127]``。 -- 每个 32 字节块(2 个相邻的 16 字节 AES 块)都由一个特殊的密钥进行加密。该密钥由 eFuse 中 Flash 加密的主密钥产生,并随 Flash 中该字节块的偏移进行 XOR 运算(一次“密钥调整”)。 + - Flash 加密中使用了逆向 AES 算法,因此 flash 加密的“加密”操作相当于 AES 解密,而其“解密”操作则相当于 AES 加密。这是为了优化性能,不会影响算法的有效性。 -- 具体调整量取决于 ``FLASH_CRYPT_CONFIG`` eFuse 的设置。该 eFuse 共 4 位,每位可对特定范围的密钥位进行 XOR 运算: + - 每个 32 字节块(2 个相邻的 16 字节 AES 块)都由一个特殊的密钥进行加密。该密钥由 ``flash_encryption`` 中 flash 加密的主密钥产生,并随 flash 中该字节块的偏移进行 XOR 运算(一次“密钥调整”)。 - - Bit 1,对密钥的 0-66 位进行 XOR 运算。 - - Bit 2,对密钥的 67-131 位进行 XOR 运算。 - - Bit 3,对密钥的 132-194 位进行 XOR 运算。 - - Bit 4,对密钥的 195-256 位进行 XOR 运算。 + - 具体调整量取决于 ``FLASH_CRYPT_CONFIG`` eFuse 的设置。该 eFuse 共 4 位,每位可对特定范围的密钥位进行 XOR 运算: - 建议将 ``FLASH_CRYPT_CONFIG`` 的值始终保留为默认值 `0xF`,这样所有密钥位都随块偏移进行 XOR 运算。详情可参见 :ref:`setting-flash-crypt-config`。 + - Bit 1,对密钥的 0-66 位进行 XOR 运算。 + - Bit 2,对密钥的 67-131 位进行 XOR 运算。 + - Bit 3,对密钥的 132-194 位进行 XOR 运算。 + - Bit 4,对密钥的 195-256 位进行 XOR 运算。 -- 块偏移的 19 个高位(第 5-23 位)由 Flash 加密的主密钥进行 XOR 运算。选定该范围的原因为:Flash 的最大尺寸为 16MB(24 位),每个块大小为 32 字节,因而 5 个最低有效位始终为 0。 + 建议将 ``FLASH_CRYPT_CONFIG`` 的值始终保留为默认值 ``0xF``,这样所有密钥位都随块偏移进行 XOR 运算。详情可参见 :ref:`setting-flash-crypt-config`。 -- 从 19 个块偏移位中每个位到 Flash 加密密钥的 256 位都有一个特殊的映射,以决定与哪个位进行 XOR 运算。有关完整映射可参见 ``espsecure.py`` 源代码中的变量 ``_FLASH_ENCRYPTION_TWEAK_PATTERN``。 + - 块偏移的 19 个高位(第 5-23 位)由 flash 加密的主密钥进行 XOR 运算。选定该范围的原因为:flash 的大小最大为 16 MB(24 位),每个块大小为 32 字节,因而 5 个最低有效位始终为 0。 -- 有关在 Python 中实现的完整 Flash 加密算法,可参见 ``espsecure.py`` 源代码中的函数 `_flash_encryption_operation()`。 + - 从 19 个块偏移位中每个位到 flash 加密密钥的 256 位都有一个特殊的映射,以决定与哪个位进行 XOR 运算。有关完整映射可参见 ``espsecure.py`` 源代码中的变量 ``_FLASH_ENCRYPTION_TWEAK_PATTERN``。 + +- 有关在 Python 中实现的完整 flash 加密算法,可参见 ``espsecure.py`` 源代码中的函数 ``_flash_encryption_operation()``。 + +.. only:: SOC_FLASH_ENCRYPTION_XTS_AES_256 + + .. _flash-encryption-algorithm: + + Flash 加密算法 + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + + - {IDF_TARGET_NAME} 使用 XTS-AES 块密码模式进行 flash 加密,密钥大小为 256 位或 512 位。 + + - XTS-AES 是一种专门为光盘加密设计的块密码模式,它解决了其它潜在模式如 AES-CTR 在此使用情景下的不足。有关 XTS-AES 算法的详细描述,请参考 `IEEE Std 1619-2007 `_。 + + - Flash 加密的密钥存储于一个或两个 ``BLOCK_KEYN`` eFuse 中,默认受保护防止进一步写入或软件读取。 + + - 有关在 Python 中实现的完整 flash 加密算法,可参见 ``espsecure.py`` 源代码中的函数 ``_flash_encryption_operation()``。 + +.. only:: SOC_FLASH_ENCRYPTION_XTS_AES and not SOC_FLASH_ENCRYPTION_XTS_AES_256 + + .. _flash-encryption-algorithm: + + Flash 加密算法 + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + + - {IDF_TARGET_NAME} 使用 XTS-AES 块密码模式进行 flash 加密,密钥大小为 256 位。 + + - XTS-AES 是一种专门为光盘加密设计的块密码模式,它解决了其它潜在模式如 AES-CTR 在此使用情景下的不足。有关 XTS-AES 算法的详细描述,请参考 `IEEE Std 1619-2007 `_。 + + - Flash 加密的密钥存储于一个 ``BLOCK_KEYN`` eFuse 中,默认受保护防止进一步写入或软件读取。 + + - 有关在 Python 中实现的完整 flash 加密算法,可参见 ``espsecure.py`` 源代码中的函数 ``_flash_encryption_operation()``。