Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ESP32] Support flash encryption and nvs encryption #20530

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions examples/lighting-app/esp32/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This example demonstrates the Matter Lighting application on ESP platforms.
- [Commissioning over BLE using chip-tool](#commissioning-over-ble-using-chip-tool)
- [Cluster Control](#cluster-control)
- [Steps to Try Lighting app OTA Requestor](#steps-to-try-lighting-app-ota-requestor)
- [Flash Encryption](#flash-encryption)

---

Expand Down Expand Up @@ -255,3 +256,50 @@ type below query.
Once the transfer is complete, OTA requestor sends ApplyUpdateRequest command to
OTA provider for applying the image. Device will restart on successful
application of OTA image.

# Flash encryption

Below is the quick start guide for encrypting the application and factory
partition but before proceeding further please READ THE DOCS FIRST.
Documentation References:

- [Flash Encryption](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/flash-encryption.html)
- [NVS Encryption](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/nvs_flash.html#nvs-encryption)

## Enable flash encryption and some factory settings using `idf.py menuconfig`

- Enable the Flash encryption [Security features → Enable flash encryption on
boot]
- Use `partitions_encrypted.csv` partition table [Partition Table → Custom
partition CSV file]
- Enable ESP32 Factory Data Provider [Component config → CHIP Device Layer →
Commissioning options → Use ESP32 Factory Data Provider]
- Enable ESP32 Device Instance Info Provider [Component config → CHIP Device
Layer → Commissioning options → Use ESP32 Device Instance Info Provider]

## Generate the factory partition using `generate_esp32_chip_factory_bin.py` script

- Please provide `-e` option along with other options to generate the
encrypted factory partition
- Two partition binaries will be generated `factory_partition.bin` and
`keys/nvs_key_partition.bin`

## Flashing the application, factory partition, and nvs keys

- Flash the application using `idf.py flash`, NOTE: If not flashing for the
first time you will have to use `idf.py encrypted-flash`

- Flash the factory partition, this SHALL be non encrypted write as NVS
encryption works differently

```
esptool.py -p (PORT) write_flash 0x9000 path/to/factory_partition.bin
```

- Encrypted flash the nvs keys partition

```
esptool.py -p (PORT) write_flash --encrypt 0x317000 path/to/nvs_key_partition.bin
```

NOTE: Above command uses the default addressed printed in the boot logs
8 changes: 8 additions & 0 deletions examples/lighting-app/esp32/partitions_encrypted.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, , 0x6000,
otadata, data, ota, , 0x2000, encrypted
phy_init, data, phy, , 0x1000, encrypted
ota_0, app, ota_0, , 1500K, encrypted
ota_1, app, ota_1, , 1500K, encrypted
nvs_key, data, nvs_keys,, 0x1000, encrypted
54 changes: 36 additions & 18 deletions scripts/tools/generate_esp32_chip_factory_bin.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
TOOLS = {}


FACTORY_PARTITION_CSV = 'nvs_partition.csv'
FACTORY_PARTITION_BIN = 'factory_partition.bin'
NVS_KEY_PARTITION_BIN = 'nvs_key_partition.bin'


FACTORY_DATA = {
# CommissionableDataProvider
'discriminator': {
Expand Down Expand Up @@ -262,28 +267,39 @@ def generate_nvs_bin(args):
continue
csv_content += f"{k},{v['type']},{v['encoding']},{v['value']}\n"

with open('nvs_partition.csv', 'w') as f:
with open(FACTORY_PARTITION_CSV, 'w') as f:
f.write(csv_content)

nvs_args = SimpleNamespace(input='nvs_partition.csv',
output='partition.bin',
size=hex(args.size),
outdir=os.getcwd(),
version=2)

nvs_partition_gen.generate(nvs_args)


def print_flashing_help():
logging.info('To flash the generated partition.bin, run the following command:')
logging.info('==============================================================')
logging.info('esptool.py -p <port> write_flash <addr> partition.bin')
logging.info('==============================================================')
logging.info('default \"nvs\" partition addr is 0x9000')
if args.encrypt:
nvs_args = SimpleNamespace(version=2,
keygen=True,
keyfile=NVS_KEY_PARTITION_BIN,
inputkey=None,
outdir=os.getcwd(),
input=FACTORY_PARTITION_CSV,
output=FACTORY_PARTITION_BIN,
size=hex(args.size))
nvs_partition_gen.encrypt(nvs_args)
else:
nvs_args = SimpleNamespace(input=FACTORY_PARTITION_CSV,
output=FACTORY_PARTITION_BIN,
size=hex(args.size),
outdir=os.getcwd(),
version=2)
nvs_partition_gen.generate(nvs_args)


def print_flashing_help(encrypt):
logging.info('Run below command to flash {}'.format(FACTORY_PARTITION_BIN))
logging.info('esptool.py -p (PORT) write_flash (FACTORY_PARTITION_ADDR) {}'.format(os.path.join(os.getcwd(), FACTORY_PARTITION_BIN)))
if (encrypt):
logging.info('Run below command to flash {}'.format(NVS_KEY_PARTITION_BIN))
logging.info('esptool.py -p (PORT) write_flash --encrypt (NVS_KEY_PARTITION_ADDR) {}'.format(
os.path.join(os.getcwd(), 'keys', NVS_KEY_PARTITION_BIN)))


def clean_up():
os.remove('nvs_partition.csv')
os.remove(FACTORY_PARTITION_CSV)
os.remove(FACTORY_DATA['dac-pub-key']['value'])
os.remove(FACTORY_DATA['dac-key']['value'])

Expand Down Expand Up @@ -323,6 +339,8 @@ def any_base_int(s): return int(s, 0)

parser.add_argument('-s', '--size', type=any_base_int, required=False, default=0x6000,
help='The size of the partition.bin, default: 0x6000')
parser.add_argument('-e', '--encrypt', action='store_true', required=False,
help='Encrypt the factory parititon NVS binary')

args = parser.parse_args()
validate_args(args)
Expand All @@ -331,7 +349,7 @@ def any_base_int(s): return int(s, 0)
populate_factory_data(args, spake2p_params)
gen_raw_ec_keypair_from_der(args.dac_key, FACTORY_DATA['dac-pub-key']['value'], FACTORY_DATA['dac-key']['value'])
generate_nvs_bin(args)
print_flashing_help()
print_flashing_help(args.encrypt)
clean_up()


Expand Down
51 changes: 51 additions & 0 deletions src/platform/ESP32/ConfigurationManagerImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,56 @@ CHIP_ERROR ConfigurationManagerImpl::Init()
CHIP_ERROR err;
uint32_t rebootCount;

#ifdef CONFIG_NVS_ENCRYPTION
nvs_sec_cfg_t cfg = {};
esp_err_t esp_err = ESP_FAIL;

const esp_partition_t * key_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS, NULL);
if (key_part == NULL)
{
ChipLogError(DeviceLayer,
"CONFIG_NVS_ENCRYPTION is enabled, but no partition with subtype nvs_keys found in the partition table.");
SuccessOrExit(MapConfigError(esp_err));
}

esp_err = nvs_flash_read_security_cfg(key_part, &cfg);
if (esp_err == ESP_ERR_NVS_KEYS_NOT_INITIALIZED)
{
ChipLogError(DeviceLayer, "NVS key partition empty");
SuccessOrExit(MapConfigError(esp_err));
}
else if (esp_err != ESP_OK)
{
ChipLogError(DeviceLayer, "Failed to read NVS security cfg, err:0x%02x", esp_err);
SuccessOrExit(MapConfigError(esp_err));
}

// Securely initialize the nvs partitions,
// nvs_flash_secure_init_partition() will initialize the partition only if it is not already initialized.
esp_err = nvs_flash_secure_init_partition(CHIP_DEVICE_CONFIG_CHIP_FACTORY_NAMESPACE_PARTITION, &cfg);
if (esp_err == ESP_ERR_NVS_NO_FREE_PAGES || esp_err == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ChipLogError(DeviceLayer, "Failed to initialize NVS partition %s err:0x%02x",
CHIP_DEVICE_CONFIG_CHIP_FACTORY_NAMESPACE_PARTITION, esp_err);
SuccessOrExit(MapConfigError(esp_err));
}

esp_err = nvs_flash_secure_init_partition(CHIP_DEVICE_CONFIG_CHIP_CONFIG_NAMESPACE_PARTITION, &cfg);
if (esp_err == ESP_ERR_NVS_NO_FREE_PAGES || esp_err == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ChipLogError(DeviceLayer, "Failed to initialize NVS partition %s err:0x%02x",
CHIP_DEVICE_CONFIG_CHIP_CONFIG_NAMESPACE_PARTITION, esp_err);
SuccessOrExit(MapConfigError(esp_err));
}

esp_err = nvs_flash_secure_init_partition(CHIP_DEVICE_CONFIG_CHIP_COUNTERS_NAMESPACE_PARTITION, &cfg);
if (esp_err == ESP_ERR_NVS_NO_FREE_PAGES || esp_err == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ChipLogError(DeviceLayer, "Failed to initialize NVS partition %s err:0x%02x",
CHIP_DEVICE_CONFIG_CHIP_COUNTERS_NAMESPACE_PARTITION, esp_err);
SuccessOrExit(MapConfigError(esp_err));
}
#else
// Initialize the nvs partitions,
// nvs_flash_init_partition() will initialize the partition only if it is not already initialized.
esp_err_t esp_err = nvs_flash_init_partition(CHIP_DEVICE_CONFIG_CHIP_FACTORY_NAMESPACE_PARTITION);
Expand All @@ -69,6 +119,7 @@ CHIP_ERROR ConfigurationManagerImpl::Init()
SuccessOrExit(MapConfigError(esp_err));
esp_err = nvs_flash_init_partition(CHIP_DEVICE_CONFIG_CHIP_COUNTERS_NAMESPACE_PARTITION);
SuccessOrExit(MapConfigError(esp_err));
#endif

// Force initialization of NVS namespaces if they doesn't already exist.
err = ESP32Config::EnsureNamespace(ESP32Config::kConfigNamespace_ChipFactory);
Expand Down