Docs/add cn trans for api reference/storage/nvs encryption.rst

This commit is contained in:
Ren Pei Ying 2023-09-11 11:02:18 +08:00
parent 51846b497b
commit 532d3f51f6
2 changed files with 245 additions and 37 deletions

View File

@ -1,24 +1,26 @@
NVS Encryption
==============
:link_to_translation:`en:[English]`
Overview
--------
This guide provides an overview of the NVS Encryption feature. NVS encryption helps to achieve secure storage on the device flash memory.
This guide provides an overview of the NVS encryption feature. NVS encryption helps to achieve secure storage on the device flash memory.
Data stored in NVS partitions can be encrypted using XTS-AES in the manner similar to the one mentioned in disk encryption standard IEEE P1619. For the purpose of encryption, each entry is treated as one `sector` and relative address of the entry (w.r.t., partition-start) is fed to the encryption algorithm as `sector-number`.
Data stored in NVS partitions can be encrypted using XTS-AES in the manner similar to the one mentioned in disk encryption standard IEEE P1619. For the purpose of encryption, each entry is treated as one ``sector`` and relative address of the entry (w.r.t., partition-start) is fed to the encryption algorithm as ``sector-number``.
.. only:: SOC_HMAC_SUPPORTED
NVS Encryption can be facilitated by enabling :ref:`CONFIG_NVS_ENCRYPTION` and :ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME` > ``CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC`` or ``CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC`` depending on the scheme to be used.
NVS encryption can be facilitated by enabling :ref:`CONFIG_NVS_ENCRYPTION` and :ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME` > ``CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC`` or ``CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC`` depending on the scheme to be used.
NVS Encryption: Flash Encryption-based Scheme
NVS Encryption: Flash Encryption-Based Scheme
---------------------------------------------
In this scheme, the keys required for NVS encryption are stored in yet another partition, which is protected using :doc:`Flash Encryption <../../security/flash-encryption>`. Therefore, enabling :doc:`Flash Encryption <../../security/flash-encryption>` becomes a prerequisite for NVS encryption here.
The NVS Encryption is enabled by default when :doc:`Flash Encryption <../../security/flash-encryption>` is enabled. This is done because Wi-Fi driver stores credentials (like SSID and passphrase) in the default NVS partition. It is important to encrypt them as default choice if platform level encryption is already enabled.
NVS encryption is enabled by default when :doc:`../../security/flash-encryption` is enabled. This is done because Wi-Fi driver stores credentials (like SSID and passphrase) in the default NVS partition. It is important to encrypt them as default choice if platform level encryption is already enabled.
For using NVS encryption using this scheme, the partition table must contain the :ref:`nvs_encr_key_partition`. Two partition tables containing the :ref:`nvs_encr_key_partition` are provided for NVS encryption under the partition table option (``menuconfig`` > ``Partition Table``). They can be selected with the project configuration menu (``idf.py menuconfig``). Please refer to the example :example:`security/flash_encryption` for how to configure and use the NVS encryption feature.
@ -27,7 +29,7 @@ For using NVS encryption using this scheme, the partition table must contain the
NVS Key Partition
^^^^^^^^^^^^^^^^^
An application requiring NVS encryption support (using the Flash Encryption-based scheme) needs to be compiled with a key-partition of the type `data` and subtype `key`. This partition should be marked as `encrypted` and its size should be the minimum partition size (4KB). Refer to :doc:`Partition Tables <../../api-guides/partition-tables>` for more details. Two additional partition tables which contain the :ref:`nvs_encr_key_partition` are provided under the partition table option (``menuconfig`` > ``Partition Table``). They can be directly used for NVS encryption. The structure of these partitions is depicted below.
An application requiring NVS encryption support (using the Flash Encryption-based scheme) needs to be compiled with a key-partition of the type ``data`` and subtype ``key``. This partition should be marked as ``encrypted`` and its size should be the minimum partition size (4 KB). Refer to :doc:`../../api-guides/partition-tables` for more details. Two additional partition tables which contain the :ref:`nvs_encr_key_partition` are provided under the partition table option (``menuconfig`` > ``Partition Table``). They can be directly used for NVS encryption. The structure of these partitions is depicted below:
.. highlight:: none
@ -45,7 +47,7 @@ The XTS encryption keys in the :ref:`nvs_encr_key_partition` can be generated in
**Generate the keys on {IDF_TARGET_NAME} chip itself**
* When NVS encryption is enabled the :cpp:func:`nvs_flash_init` API function can be used to initialize the encrypted default NVS partition. The API function internally generates the XTS encryption keys on the ESP chip. The API function finds the first :ref:`nvs_encr_key_partition`.
* When NVS encryption is enabled, the :cpp:func:`nvs_flash_init` API function can be used to initialize the encrypted default NVS partition. The API function internally generates the XTS encryption keys on the ESP chip. The API function finds the first :ref:`nvs_encr_key_partition`.
* Then the API function automatically generates and stores the NVS keys in that partition by making use of the :cpp:func:`nvs_flash_generate_keys` API function provided by :component_file:`nvs_flash/include/nvs_flash.h`. New keys are generated and stored only when the respective key partition is empty. The same key partition can then be used to read the security configurations for initializing a custom encrypted NVS partition with help of :cpp:func:`nvs_flash_secure_init_partition`.
@ -53,64 +55,65 @@ The XTS encryption keys in the :ref:`nvs_encr_key_partition` can be generated in
.. note::
Please note that ``nvs_keys`` partition must be completely erased before you start the application in this approach. Otherwise the application may generate :c:macro:`ESP_ERR_NVS_CORRUPT_KEY_PART` error code assuming that ``nvs_keys`` partition is not empty and contains malformatted data. You can use the following command for this:
Please note that ``nvs_keys`` partition must be completely erased before you start the application in this approach. Otherwise the application may generate the :c:macro:`ESP_ERR_NVS_CORRUPT_KEY_PART` error code assuming that ``nvs_keys`` partition is not empty and contains malformatted data. You can use the following command for this:
::
parttool.py --port PORT --partition-table-file=PARTITION_TABLE_FILE --partition-table-offset PARTITION_TABLE_OFFSET erase_partition --partition-type=data --partition-subtype=nvs_keys
**Use a pre-generated NVS key partition**
This option will be required by the user when keys in the :ref:`nvs_encr_key_partition` are not generated by the application. The :ref:`nvs_encr_key_partition` containing the XTS encryption keys can be generated with the help of :doc:`NVS Partition Generator Utility</api-reference/storage/nvs_partition_gen>`. Then the user can store the pre generated key partition on the flash with help of the following two commands:
This option will be required by the user when keys in the :ref:`nvs_encr_key_partition` are not generated by the application. The :ref:`nvs_encr_key_partition` containing the XTS encryption keys can be generated with the help of :doc:`NVS Partition Generator Utility </api-reference/storage/nvs_partition_gen>`. Then the user can store the pre-generated key partition on the flash with help of the following two commands:
1. Build and flash the partition table
::
idf.py partition-table partition-table-flash
2. Store the keys in the :ref:`nvs_encr_key_partition` (on the flash) with the help of :component_file:`parttool.py<partition_table/parttool.py>` (see Partition Tool section in :doc:`partition-tables </api-guides/partition-tables>` for more details)
2. Store the keys in the :ref:`nvs_encr_key_partition` (on the flash) with the help of :component_file:`parttool.py <partition_table/parttool.py>` (see Partition Tool section in :doc:`partition-tables </api-guides/partition-tables>` for more details)
::
parttool.py --port PORT --partition-table-offset PARTITION_TABLE_OFFSET write_partition --partition-name="name of nvs_key partition" --input NVS_KEY_PARTITION_FILE
.. note:: If the device is encrypted in flash encryption development mode and you want to renew the NVS key partition, you need to tell :component_file:`parttool.py <partition_table/parttool.py>` to encrypt the NVS key partition and you also need to give it a pointer to the unencrypted partition table in your build directory (build/partition_table) since the partition table on the device is encrypted, too. You can use the following command:
.. note::
If the device is encrypted in flash encryption development mode and you want to renew the NVS key partition, you need to tell :component_file:`parttool.py<partition_table/parttool.py>` to encrypt the NVS key partition and you also need to give it a pointer to the unencrypted partition table in your build directory (build/partition_table) since the partition table on the device is encrypted, too. You can use the following command:
::
parttool.py --esptool-write-args encrypt --port PORT --partition-table-file=PARTITION_TABLE_FILE --partition-table-offset PARTITION_TABLE_OFFSET write_partition --partition-name="name of nvs_key partition" --input NVS_KEY_PARTITION_FILE
Since the key partition is marked as `encrypted` and :doc:`Flash Encryption <../../security/flash-encryption>` is enabled, the bootloader will encrypt this partition using flash encryption key on the first boot.
Since the key partition is marked as ``encrypted`` and :doc:`Flash Encryption <../../security/flash-encryption>` is enabled, the bootloader will encrypt this partition using flash encryption key on the first boot.
It is possible for an application to use different keys for different NVS partitions and thereby have multiple key-partitions. However, it is a responsibility of the application to provide correct key-partition/keys for the purpose of encryption/decryption.
It is possible for an application to use different keys for different NVS partitions and thereby have multiple key-partitions. However, it is a responsibility of the application to provide the correct key-partition and keys for encryption or decryption.
.. only:: SOC_HMAC_SUPPORTED
NVS Encryption: HMAC Peripheral-Based Scheme
--------------------------------------------
NVS Encryption: HMAC Peripheral-Based Scheme
--------------------------------------------
In this scheme, the XTS keys required for NVS encryption are derived from an HMAC key programmed in eFuse with the purpose :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP`. Since the encryption keys are derived at runtime, they are not stored anywhere in the flash. Thus, this feature does not require a separate :ref:`nvs_encr_key_partition`.
In this scheme, the XTS keys required for NVS encryption are derived from an HMAC key programmed in eFuse with the purpose :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP`. Since the encryption keys are derived at runtime, they are not stored anywhere in the flash. Thus, this feature does not require a separate :ref:`nvs_encr_key_partition`.
.. note::
.. note::
This scheme enables us to achieve secure storage on {IDF_TARGET_NAME} **without enabling flash encryption**.
This scheme enables us to achieve secure storage on {IDF_TARGET_NAME} **without enabling flash encryption**.
.. important::
.. important::
Please take note that this scheme uses one eFuse block for storing the HMAC key required for deriving the encryption keys.
Please take note that this scheme uses one eFuse block for storing the HMAC key required for deriving the encryption keys.
- When NVS encryption is enabled, the :cpp:func:`nvs_flash_init` API function can be used to initialize the encrypted default NVS partition. The API function first checks whether an HMAC key is present at :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID`.
.. note::
.. note::
The valid range for the config :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` is from ``0`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY0`) to ``5`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY5`). By default, the config is set to ``6`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY_MAX`), which have to be configured before building the user application.
The valid range for the config :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` is from ``0`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY0`) to ``5`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY5`). By default, the config is set to ``6`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY_MAX`), which have to be configured before building the user application.
- If no key is found, a key is generated internally and stored at the eFuse block specified at :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID`.
- If a key is found with the purpose :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP`, the same is used for the derivation of the XTS encryption keys.
- If the specified eFuse block is found to be occupied with a key with a purpose other than :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP`, an error is thrown.
- If no key is found, a key is generated internally and stored at the eFuse block specified at :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID`.
- If a key is found with the purpose :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP`, the same is used for the derivation of the XTS encryption keys.
- If the specified eFuse block is found to be occupied with a key with a purpose other than :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP`, an error is thrown.
- The API :cpp:func:`nvs_flash_init` then automatically generates the NVS keys on demand by using the :cpp:func:`nvs_flash_generate_keys_v2` API function provided by the :component_file:`nvs_flash/include/nvs_flash.h`. The same keys can also be used to read the security configurations (see :cpp:func:`nvs_flash_read_security_cfg_v2`) for initializing a custom encrypted NVS partition with help of :cpp:func:`nvs_flash_secure_init_partition`.
- The API functions :cpp:func:`nvs_flash_secure_init` and :cpp:func:`nvs_flash_secure_init_partition` do not generate the keys internally. When these API functions are used for initializing encrypted NVS partitions, the keys can be generated after startup using the :cpp:func:`nvs_flash_generate_keys_v2` API function or take and populate the NVS security configuration structure :cpp:type:`nvs_sec_cfg_t` with :cpp:func:`nvs_flash_read_security_cfg_v2` and feed them into the above APIs.
.. note:: Users can program their own HMAC key in eFuse block beforehand by using the following command -
.. note:: Users can program their own HMAC key in eFuse block beforehand by using the following command:
::
espefuse.py -p PORT burn_key <BLOCK_KEYN> <hmac_key_file.bin> HMAC_UP
@ -118,11 +121,11 @@ It is possible for an application to use different keys for different NVS partit
Encrypted Read/Write
--------------------
The same NVS API functions ``nvs_get_*`` or ``nvs_set_*`` can be used for reading of, and writing to an encrypted nvs partition as well.
The same NVS API functions ``nvs_get_*`` or ``nvs_set_*`` can be used for reading of, and writing to an encrypted NVS partition as well.
**Encrypt the default NVS partition**
- To enable encryption for the default NVS partition no additional steps are necessary. When :ref:`CONFIG_NVS_ENCRYPTION` is enabled, the :cpp:func:`nvs_flash_init` API function internally performs some additional steps to enable encryption for the default NVS partition depending on the scheme being used (set by :ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME`).
- To enable encryption for the default NVS partition, no additional step is necessary. When :ref:`CONFIG_NVS_ENCRYPTION` is enabled, the :cpp:func:`nvs_flash_init` API function internally performs some additional steps to enable encryption for the default NVS partition depending on the scheme being used (set by :ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME`).
- For the flash encryption-based scheme, the first :ref:`nvs_encr_key_partition` found is used to generate the encryption keys while for the HMAC one, keys are generated using the HMAC key burnt in eFuse at :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` (refer to the API documentation for more details).
@ -132,23 +135,23 @@ Alternatively, :cpp:func:`nvs_flash_secure_init` API function can also be used t
- To enable encryption for a custom NVS partition, :cpp:func:`nvs_flash_secure_init_partition` API function is used instead of :cpp:func:`nvs_flash_init_partition`.
- When :cpp:func:`nvs_flash_secure_init` and :cpp:func:`nvs_flash_secure_init_partition` API functions are used, the applications are expected to follow the steps below in order to perform NVS read/write operations with encryption enabled.
- When :cpp:func:`nvs_flash_secure_init` and :cpp:func:`nvs_flash_secure_init_partition` API functions are used, the applications are expected to follow the steps below in order to perform NVS read/write operations with encryption enabled:
1. Populate the NVS security configuration structure :cpp:type:`nvs_sec_cfg_t`
* For the Flash Encryption-based scheme -
* For the Flash Encryption-based scheme
- Find key partition and NVS data partition using ``esp_partition_find*`` API functions.
- Populate the :cpp:type:`nvs_sec_cfg_t` struct using the :cpp:func:`nvs_flash_read_security_cfg` or :cpp:func:`nvs_flash_generate_keys` API functions.
.. only:: SOC_HMAC_SUPPORTED
* For the HMAC-based scheme -
* For the HMAC-based scheme
- Set the scheme-specific config data with :cpp:type:`nvs_sec_config_hmac_t` and register the HMAC-based scheme with the API :cpp:func:`nvs_sec_provider_register_hmac` which will also populate the scheme-specific handle (see :cpp:type:`nvs_sec_scheme_t`).
- Populate the :cpp:type:`nvs_sec_cfg_t` struct using the :cpp:func:`nvs_flash_read_security_cfg_v2` or :cpp:func:`nvs_flash_generate_keys_v2` API functions.
- Set the scheme-specific config data with :cpp:type:`nvs_sec_config_hmac_t` and register the HMAC-based scheme with the API :cpp:func:`nvs_sec_provider_register_hmac` which will also populate the scheme-specific handle (see :cpp:type:`nvs_sec_scheme_t`).
- Populate the :cpp:type:`nvs_sec_cfg_t` struct using the :cpp:func:`nvs_flash_read_security_cfg_v2` or :cpp:func:`nvs_flash_generate_keys_v2` API functions.
.. code-block:: c
.. code-block:: c
nvs_sec_cfg_t cfg = {};
nvs_sec_scheme_t *sec_scheme_handle = NULL;
@ -180,9 +183,10 @@ Alternatively, :cpp:func:`nvs_flash_secure_init` API function can also be used t
4. Perform NVS read/write operations using ``nvs_get_*`` or ``nvs_set_*``.
5. Deinitialise an NVS partition using :cpp:func:`nvs_flash_deinit`.
.. only:: SOC_HMAC_SUPPORTED
.. only:: SOC_HMAC_SUPPORTED
.. note:: While using the HMAC-based scheme, the above workflow can be used without enabling any of the config options for NVS Encryption - :ref:`CONFIG_NVS_ENCRYPTION`, :ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME` -> ``CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC`` and :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` to encrypt the default as well as custom NVS partitions with :cpp:func:`nvs_flash_secure_init` API.
.. note::
While using the HMAC-based scheme, the above workflow can be used without enabling any of the config options for NVS encryption - :ref:`CONFIG_NVS_ENCRYPTION`, :ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME` -> ``CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC`` and :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` to encrypt the default as well as custom NVS partitions with :cpp:func:`nvs_flash_secure_init` API.
NVS Security Provider

View File

@ -1 +1,205 @@
.. include:: ../../../en/api-reference/storage/nvs_encryption.rst
NVS 加密
==============
:link_to_translation:`en:[English]`
概述
--------
本文档主要介绍 NVS 加密功能,这一功能有助于实现设备在 flash 中的安全存储。
存储在 NVS 分区中的数据可以用 XTS-AES 进行加密,与磁盘加密标准 IEEE P1619 中提到的加密方式类似。加密时,每个条目都被视作一个 ``sector``,而条目的相对地址(相对于分区起始位置)作为 ``sector-number`` 输入加密算法。
.. only:: SOC_HMAC_SUPPORTED
根据要使用的具体方案,可以选择启用 :ref:`CONFIG_NVS_ENCRYPTION`:ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME` > ``CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC````CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC`` 实现 NVS 加密。
NVS 加密:基于 flash 加密的方案
-------------------------------------
在这个方案中NVS 加密所需的密钥存储在另一个分区中,该分区用 :doc:`../../security/flash-encryption` 进行保护。因此,使用该方案时,必须先启用 :doc:`../../security/flash-encryption`
启用 :doc:`../../security/flash-encryption` 时,默认启用 NVS 加密。这是因为 Wi-Fi 驱动程序会将凭证(如 SSID 和密码)储存在默认的 NVS 分区中。如已启用平台级加密,那么同时默认启用 NVS 加密有其必要性。
要用这一方案进行 NVS 加密,分区表中必须包含 :ref:`nvs_encr_key_partition`。在分区表选项 ( ``menuconfig`` > ``Partition Table`` ) 中,有两个包含 :ref:`nvs_encr_key_partition` 的分区表,可通过项目配置菜单 ( ``idf.py menuconfig``) 进行选择。要了解如何配置和使用 NVS 加密功能,请参考示例 :example:`security/flash_encryption`
.. _nvs_encr_key_partition:
NVS 密钥分区
^^^^^^^^^^^^^^^^^
应用如果要使用 NVS 加密(使用基于 flash 加密的方案)编译时,须使用类型为 ``data`` 和子类型为 ``key`` 的密钥分区。该分区应被标记为 ``encrypted`` 且最小为 4 KB (最小分区大小)。参考 :doc:`../../api-guides/partition-tables` 了解详情。在分区表选项 ( ``menuconfig`` > ``Partition Table``) 中,有两个包含 :ref:`nvs_encr_key_partition` 的额外分区表,可以直接用于 NVS 加密。分区的结构如下所示:
.. highlight:: none
::
+-----------+--------------+-------------+----+
| XTS encryption key (32) |
+---------------------------------------------+
| XTS tweak key (32) |
+---------------------------------------------+
| CRC32 (4) |
+---------------------------------------------+
可以通过以下两种方式之一生成 :ref:`nvs_encr_key_partition` 中的 XTS 加密密钥:
**在 {IDF_TARGET_NAME} 芯片上生成密钥**
* 启用 NVS 加密时,可使用 API 函数 :cpp:func:`nvs_flash_init` 来初始化加密的默认 NVS 分区。该 API 函数会内部生成 ESP 芯片的 XTS 加密密钥,并找到第一个 :ref:`nvs_encr_key_partition`
* 然后,该 API 函数使用 :component_file:`nvs_flash/include/nvs_flash.h` 提供的 :cpp:func:`nvs_flash_generate_keys` API 函数,自动生成 NVS 密钥并存储到该分区。只有当相应的密钥分区为空时,才会生成和存储新密钥。然后,就可以利用 :cpp:func:`nvs_flash_secure_init_partition` 用此密钥分区来读取安全配置,以初始化自定义的加密 NVS 分区。
* API 函数 :cpp:func:`nvs_flash_secure_init`:cpp:func:`nvs_flash_secure_init_partition` 不会内部生成密钥。如果要使用这两个 API 函数初始化加密的 NVS 分区,可以在启动后使用 ``nvs_flash.h`` 提供的 :cpp:func:`nvs_flash_generate_keys` API 函数生成密钥,然后由该 API 函数将生成的密钥以加密形式写入密钥分区中。
.. note::
请注意,在使用此方法启动应用程序前,必须完全擦除 ``nvs_keys`` 分区。否则,应用程序可能会生成 :c:macro:`ESP_ERR_NVS_CORRUPT_KEY_PART` 错误代码,该代码假设 ``nvs_keys`` 分区不为空并且包含格式错误的数据。可以使用以下命令来实现:
::
parttool.py --port PORT --partition-table-file=PARTITION_TABLE_FILE --partition-table-offset PARTITION_TABLE_OFFSET erase_partition --partition-type=data --partition-subtype=nvs_keys
**使用预生成的 NVS 密钥分区**
如果 :ref:`nvs_encr_key_partition` 中的密钥不是由应用程序生成,则需要使用预先生成的密钥分区。可以使用 :doc:`/api-reference/storage/nvs_partition_gen` 生成包含 XTS 加密密钥的 :ref:`nvs_encr_key_partition`。然后使用以下两个命令将预生成的密钥分区存储到 flash 上:
1. 构建并烧写分区表
::
idf.py partition-table partition-table-flash
1. 使用 :component_file:`parttool.py<partition_table/parttool.py>` (参见 :doc:`/api-guides/partition-tables` 中分区工具相关章节)将密钥存储在 flash 上的 :ref:`nvs_encr_key_partition`
::
parttool.py --port PORT --partition-table-offset PARTITION_TABLE_OFFSET write_partition --partition-name="name of nvs_key partition" --input NVS_KEY_PARTITION_FILE
.. note::
如果设备是在 flash 加密开发模式下加密的,那么要更新 NVS 密钥分区就需要使用 :component_file:`parttool.py <partition_table/parttool.py>` 来加密 NVS 密钥分区,并提供一个指向你构建目录中未加密分区表的指针 (build/partition_table),因为设备上的分区表也是加密的。命令如下:
::
parttool.py --esptool-write-args encrypt --port PORT --partition-table-file=PARTITION_TABLE_FILE --partition-table-offset PARTITION_TABLE_OFFSET write_partition --partition-name="nvs_key 分区名称" --input NVS_KEY_PARTITION_FILE
由于密钥分区被标记为 ``encrypted``,且 :doc:`../../security/flash-encryption` 已启用,引导程序会在首次启动时使用 flash 加密密钥对此分区进行加密。
一个应用程序可以使用不同的密钥对不同的 NVS 分区进行加密,从而拥有多个密钥分区。应用程序应为加密或解密操作提供正确的密钥分区和密钥信息。
.. only:: SOC_HMAC_SUPPORTED
NVS 加密:基于 HMAC 外设的方案
--------------------------------------------
此方案中,用于 NVS 加密的 XTS 密钥来自 eFuse 中编程的 HMAC 密钥,其目的是 :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP`。由于加密密钥在运行时生成,不存储在 flash 中,因此这个功能不需要单独的 :ref:`nvs_encr_key_partition`
.. note::
通过这个方案, **无需启用 flash 加密** 就能在 {IDF_TARGET_NAME} 上实现安全存储。
.. important::
注意,此方案使用一个 eFuse 块来存储获取加密密钥所需的 HMAC 密钥。
- NVS 加密启用时后,可用 API 函数 :cpp:func:`nvs_flash_init` 来初始化加密的默认 NVS 分区。该 API 函数首先检查 :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` 处是否存在一个 HMAC 密钥。
.. note::
:ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` 配置的有效范围为 ``0`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY0`) 到 ``5`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY5`)。默认情况下该配置为 ``6`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY_MAX`),须在构建用户应用程序之前进行修改。
- 如果找不到密钥,会内部生成一个密钥,并储存在 :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` 指定的 eFuse 块中。
- 如果找到用于 :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP` 的密钥,该密钥也会用于 XTS 加密密钥的生成。
- 如果指定的 eFuse 块被 :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP` 以外目的的密钥占用,则会引发错误。
- 然后API :cpp:func:`nvs_flash_init` 使用 :component_file:`nvs_flash/include/nvs_flash.h` 提供的 :cpp:func:`nvs_flash_generate_keys_v2` API 函数,自动生成所需的 NVS 密钥。该密钥还可用于读取安全配置(参见 :cpp:func:`nvs_flash_read_security_cfg_v2` 并通过 :cpp:func:`nvs_flash_secure_init_partition` 初始化自定义的加密 NVS 分区。
- API 函数 :cpp:func:`nvs_flash_secure_init`:cpp:func:`nvs_flash_secure_init_partition` 不会内部生成密钥。使用这些 API 函数初始化加密的 NVS 分区时,可在启动后用 API 函数 :cpp:func:`nvs_flash_generate_keys_v2` 生成密钥,或使用 :cpp:func:`nvs_flash_read_security_cfg_v2` 获取并填充 NVS 安全配置结构 :cpp:type:`nvs_sec_cfg_t`,将其输入到上述 API 中。
.. note:: 可以使用以下命令预先在 eFuse 中设置自己的 HMAC 密钥:
::
espefuse.py -p PORT burn_key <BLOCK_KEYN> <hmac_key_file.bin> HMAC_UP
加密读/写
--------------------
NVS API 函数 ``nvs_get_*````nvs_set_*`` 也可用于读取和写入加密的 NVS 分区。
**加密默认的 NVS 分区**
- 要为默认 NVS 分区启用加密,无需额外的步骤。在启用 :ref:`CONFIG_NVS_ENCRYPTION`API 函数 :cpp:func:`nvs_flash_init` 会根据使用的方案(由 :ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME` 设置)在内部执行一些额外步骤,为默认的 NVS 分区启用加密。
- 在基于 flash 加密的方案中,加密密钥由找到的第一个 :ref:`nvs_encr_key_partition` 生成。在 HMAC 方案中,密钥由 :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` 中烧录的 HMAC 密钥生成(参考 API 文档以了解更多详细信息)。
另外,还可使用 API 函数 :cpp:func:`nvs_flash_secure_init` 为默认 NVS 分区启用加密。
**加密自定义 NVS 分区**
- 要为一个自定义的 NVS 分区启用加密,使用 API 函数 :cpp:func:`nvs_flash_secure_init_partition` 代替 :cpp:func:`nvs_flash_init_partition`
- 使用 API 函数 :cpp:func:`nvs_flash_secure_init`:cpp:func:`nvs_flash_secure_init_partition` 时,为了在启用加密的情况下执行 NVS 读/写操作,应用程序应遵守以下步骤:
1. 填充 NVS 安全配置结构 :cpp:type:`nvs_sec_cfg_t`
* 对基于 flash 加密的方案
- 使用 API 函数 `esp_partition_find*` 查找密钥分区和 NVS 数据分区。
- 使用 API 函数 :cpp:func:`nvs_flash_read_security_cfg`:cpp:func:`nvs_flash_generate_keys` 填充 :cpp:type:`nvs_sec_cfg_t` 结构体。
.. only:: SOC_HMAC_SUPPORTED
* 对基于 HMAC 的方案
- 用 :cpp:type:`nvs_sec_config_hmac_t` 为设置特定方案配置数据,并使用 API :cpp:func:`nvs_sec_provider_register_hmac` 注册此基于 HMAC 的方案。该 API 也将用于填充特定方案的句柄(参见 :cpp:type:`nvs_sec_scheme_t`)。
- 使用 API 函数 :cpp:func:`nvs_flash_read_security_cfg_v2`:cpp:func:`nvs_flash_generate_keys_v2` 填充 :cpp:type:`nvs_sec_cfg_t` 结构体。
.. code-block:: c
nvs_sec_cfg_t cfg = {};
nvs_sec_scheme_t *sec_scheme_handle = NULL;
nvs_sec_config_hmac_t sec_scheme_cfg = {};
hmac_key_id_t hmac_key = HMAC_KEY0;
sec_scheme_cfg.hmac_key_id = hmac_key;
ret = nvs_sec_provider_register_hmac(&sec_scheme_cfg, &sec_scheme_handle);
if (ret != ESP_OK) {
return ret;
}
ret = nvs_flash_read_security_cfg_v2(sec_scheme_handle, &cfg);
if (ret != ESP_OK) {
if (ret == ESP_ERR_NVS_SEC_HMAC_KEY_NOT_FOUND) {
ret = nvs_flash_generate_keys_v2(&sec_scheme_handle, &cfg);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate NVS encr-keys!");
return ret;
}
}
ESP_LOGE(TAG, "Failed to read NVS security cfg!");
return ret;
}
2. 使用 API 函数 :cpp:func:`nvs_flash_secure_init`:cpp:func:`nvs_flash_secure_init_partition` 初始化 NVS flash 分区。
3. 使用 API 函数 :cpp:func:`nvs_open`:cpp:func:`nvs_open_from_partition` 打开一个命名空间。
4. 使用 ``nvs_get_*````nvs_set_*`` 执行 NVS 读/写操作。
5. 使用 :cpp:func:`nvs_flash_deinit` 取消初始化 NVS 分区。
.. only:: SOC_HMAC_SUPPORTED
.. note::
在采用基于 HMAC 的方案时,可以在不启用任何 NVS 加密的配置选项的情况下开始上述工作流::ref:`CONFIG_NVS_ENCRYPTION`:ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME` -> `CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC`:ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID`,以使用 :cpp:func:`nvs_flash_secure_init` API 加密默认分区及自定义的 NVS 分区。
NVS Security Provider
---------------------
组件 :component:`nvs_sec_provider` 存储了 NVS 加密方案的所有特定实现代码,并且适用于未来的方案。此组件充当 :component:`nvs_flash` 组件处理加密密钥的接口。组件 :component:`nvs_sec_provider` 有自己的配置菜单,选定的安全方案和相应设置基于这一菜单注册到 :component:`nvs_flash` 组件。
.. only:: SOC_HMAC_SUPPORTED
该组件通过工厂函数注册了特殊的安全框架,可以实现出厂即用的安全方案。在该方案中,无需使用 API 来生成、读取加密密钥(如 :cpp:func:`nvs_sec_provider_register_hmac`)。要了解 API 的使用,参考示例 :example:`security/nvs_encryption_hmac`
API 参考
-------------
.. include-build-file:: inc/nvs_sec_provider.inc