Skip to content

Commit

Permalink
[ESP32] Support flash encryption and nvs encryption (#20414)
Browse files Browse the repository at this point in the history
- Added an option to encrypt the factory partition.
- Securely initialize the NVS partition if NVS encryption is enabled
- Added user guide for flash encryption in lighting-app/esp32
  • Loading branch information
shubhamdp authored Jul 9, 2022
1 parent 5745a46 commit 776c06d
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 18 deletions.
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

0 comments on commit 776c06d

Please sign in to comment.