fix(nvs_flash): zeroize XTS contexts when encrypted partition is destroyed

NVSEncryptedPartition held two XTS_CONTEXT members (mEctxt, mDctxt) for
encryption / decryption. Their AES round keys are derived from the NVS
encryption key (HMAC-derived or plaintext from nvs_keys partition) and
therefore are sensitive secrets.

The destructor was empty, so when the NVS encrypted partition object
was destroyed -- on nvs_flash_deinit_partition(), on initialization
errors, and on any other teardown path -- the XTS round keys were left
in DRAM until the freed object's memory happened to be overwritten by
a later allocation. A subsequent stack/heap leak primitive would
recover the AES key from those bytes.

Fix:
* Initialize both XTS contexts in the constructor so the destructor's
  free path is always safe (previously xts_init was only called in
  init(); destruction before init() would have run xts_free on an
  uninitialized struct).
* Provide a real destructor that calls XTS_FUNC(xts_free) on both
  contexts, which performs mbedtls_platform_zeroize / esp_aes_xts free
  semantics on the underlying AES contexts.
This commit is contained in:
Aditya Patwardhan
2026-05-25 21:58:24 +05:30
parent 67a7700383
commit 65e77bcfd3
2 changed files with 19 additions and 3 deletions
@@ -13,12 +13,28 @@ namespace nvs {
#ifdef CONFIG_NVS_BDL_STACK
NVSEncryptedPartition::NVSEncryptedPartition(const char* label, const esp_blockdev_handle_t bdl, const bool managed_bdl)
: NVSPartition(label, bdl, managed_bdl) { }
: NVSPartition(label, bdl, managed_bdl)
{
XTS_FUNC(xts_init)(&mEctxt);
XTS_FUNC(xts_init)(&mDctxt);
}
#else
NVSEncryptedPartition::NVSEncryptedPartition(const esp_partition_t *partition)
: NVSPartition(partition) { }
: NVSPartition(partition)
{
XTS_FUNC(xts_init)(&mEctxt);
XTS_FUNC(xts_init)(&mDctxt);
}
#endif // CONFIG_NVS_BDL_STACK
NVSEncryptedPartition::~NVSEncryptedPartition()
{
/* Wipe AES round keys derived from the NVS encryption key so they are not
* left in DRAM after the partition object is destroyed. */
XTS_FUNC(xts_free)(&mEctxt);
XTS_FUNC(xts_free)(&mDctxt);
}
esp_err_t NVSEncryptedPartition::init(nvs_sec_cfg_t* cfg)
{
uint8_t* eky = reinterpret_cast<uint8_t*>(cfg);