Page 1 of 1

NVS partition encryption keys in HMAC mode

Posted: Thu Dec 28, 2023 11:18 am
by NStorm
I don't really completely understand how it works on ESP32-C3. Things that clear to me:
1. HMAC are used as a KDF to securely derive AES encryption key, which are used to encrypt/decrypt data on NVS partition.
2. The key for HMAC are stored in eFuse, read protected.

Ok, but HMAC also takes 'message' as input, besides the key. Both message and key contribute to output. So what are the 'message' in the NVS/HMAC scenario? EDIT: see below, found out that already
Second thing I don't understand is how the resulting HMAC output aka encryption key (and the data on corresponding encrypted partition) are protected from being read by flashing other APP which can just read NVS the same way as original firmware. Yes, I know that enabling secure boot or flash encryption can prevent this. But that's just another layer of protection, not mentionned on the docs. I mean I understand that key in efuse are read protected but from that I've seen on the API reference and code HMAC output are not and is just stored in RAM structure. Am I missing something or it's intended to protect data in NVS only by external dump and extra layer (like secure boot,flash encryption, etc) are required to secure it from hacked firmware on CPU?
Answer to first question might be a key for my understanding second. If there are some kind of application supplied 'message' for HMAC KDF it is basically used as 'password' to derive encryption key. But I can't find anything related on API reference.



Really would like to learn an answers on these, to get theory behind that feature. But in case someone might wonder what I'm trying to achieve - I've made a device to generate TOTP (RFC 6238) on LCD with keypad, which are used to enter PIN code. Key for the TOTP needs to be secured. Unfortunately most TOTP services use HMAC-SHA1 without options, so I can't just store it in the eFuse and use HMAC module to generate keys. So I'm stuck with looking for other options. Currently I've followed the most straighforward way (for me at least) - encrypt it with AES and store on flash. Key for the AES encryption are derived from PIN and some other data with PBKDF2. Considering adding some steps with HMAC module upstream mode for KDF as well, having it's key protected in eFuse. Besides I'm forced to keep AES encryption key and/or decrypted TOTP key in RAM for some time when the device are "unlocked" (correct PIN entered) to generate TOTP responses. But at least even without secure boot and flash encryption (I'm considering enabling them in 'release' version but as I've already said - that's just another layer) if this device gets in wrong hands the only option I see it to brute-force PIN codes which should be covered by HMAC with eFuse key step plus high enough count of iterations to slow it down. Enabling secure boot & flash encryption as well will probably provide me just enough security for the purpose - it's just a hobby project anyways, not trying to make it unbreakable by intelligence services guys :D

EDIT: I don't know how it works like this, but once I've posted that message I was able to find a piece of code in ESP-IDF which derives NVS encryption keys with HMAC. So 'message' are basically fixed 'seeds' of 0xAEBE5A5A and 0xCEDEA5A5 unfortunately:
  1. static esp_err_t compute_nvs_keys_with_hmac(hmac_key_id_t hmac_key_id, nvs_sec_cfg_t* cfg)
  2. {
  3.     uint32_t ekey_seed[8] = {[0 ... 7] = 0xAEBE5A5A};
  4.     uint32_t tkey_seed[8] = {[0 ... 7] = 0xCEDEA5A5};
  5.  
  6.     esp_err_t err = esp_hmac_calculate(hmac_key_id, ekey_seed, sizeof(ekey_seed), (uint8_t *)cfg->eky);
  7.     if (err != ESP_OK) {
  8.         ESP_LOGE(TAG, "Failed to calculate seed HMAC: [0x%02X] (%s)", err, esp_err_to_name(err));
  9.         return err;
  10.     }
  11.     ESP_FAULT_ASSERT(err == ESP_OK);
  12.  
  13.     err = esp_hmac_calculate(hmac_key_id, tkey_seed, sizeof(tkey_seed), (uint8_t *)cfg->tky);
  14.     if (err != ESP_OK) {
  15.         ESP_LOGE(TAG, "Failed to calculate seed HMAC: [0x%02X] (%s)", err, esp_err_to_name(err));
  16.         return err;
  17.     }
  18.     ESP_FAULT_ASSERT(err == ESP_OK);
  19.  
  20.     /* NOTE: If the XTS E-key and T-key are the same, we have a hash collision */
  21.     ESP_FAULT_ASSERT(memcmp(cfg->eky, cfg->tky, NVS_KEY_SIZE) != 0);
  22.  
  23.     return ESP_OK;
  24. }

Re: NVS partition encryption keys in HMAC mode

Posted: Thu Jan 04, 2024 9:14 am
by ESP_harshal
Hello @NStorm,

> I know that enabling secure boot or flash encryption can prevent this

If you do not enable secure boot, and even if you somehow read-protect the NVS encryption key (maybe by storing the encryption key directly in the efuses like the read-protected flash encryption key), the "other app" which has been flashed can still decrypt the NVS and get plaintext NVS partition. So currently even if we get the resulting HMAC output aka the encryption key, it would be of no use.

This would be a problem when you are able to derive the HMAC key that is stored in the efuse using the hardcoded seed and the HMAC output, which is very difficult (that is equivalent to breaking SHA-256) and the application is using the same efuse key for some other operation.

In case you do not want the NVS encryption key to be readable to the application and use the XTS-AES flash encryption method to encrypt the NVS and not the NVS encryption scheme, then you can take a reference to this discussion:
viewtopic.php?t=28715#p101973

Thanks!

Re: NVS partition encryption keys in HMAC mode

Posted: Tue Jan 09, 2024 6:06 am
by NStorm
Hello @ESP_harshal,
ESP_harshal wrote:
Thu Jan 04, 2024 9:14 am
If you do not enable secure boot, and even if you somehow read-protect the NVS encryption key (maybe by storing the encryption key directly in the efuses like the read-protected flash encryption key), the "other app" which has been flashed can still decrypt the NVS and get plaintext NVS partition. So currently even if we get the resulting HMAC output aka the encryption key, it would be of no use.
Yes, you are absolutely right on that. But the thing is that my goals aren't to protect NVM encryption key, but the data on the NVS itself. Because in my project NVS encryption key can be derived from external data: user should provide a valid "secret" (password/PIN code/whatever) to "unlock" the device. Same secret can be expanded to encryption key with KDF. Which are held only in RAM for a limited ammount of time and aren't stored anywhere in non-volatile memory. Device doesn't needs to be connected to network after provisioning (except for rare NTP time updates). So when a user enters the secret it can only be stolen by physical means.

I'm not sure if I'm correct on the terminology here, but in other words I'd like to integrate user authentication function with a security (encrypion). That (from my point of view, feel free to correct me if I'm wrong) should make storing sensitive data more secure. Because:

1) It is not possible to retrieve sensitive data without knowing valid secret. Well, at least that won't be easy considering encryption key and the decrypted data in the SRAM are overwritten in a few milliseconds once retrieved. Protected data are used as a key to calculate another HMAC and the user needs to see only the resulting value. Theoretically the attacker can somehow catch that moment and freeze the device while the secret or data are still in RAM and then retrieve it somehow. But that's beyond reasonable measures I'm looking for.
Yet, I have to dig further into ESP32-C3 MMU cache. Can it happen so that despite I've overwritten original SRAM location with keys, copy of it remains in cache portion for some time?
2) It protects sensitive info from the attacks like re-enabling JTAG. Or tampering eFuse with Secure Boot RSA key to forge a new key somehow. I think it actually works against every sort of attack, when a malicious person can execute arbitrary code on the device.
3) It removes the "burden" of secure authention because that effectively provides it by itself. We don't need to store hash of the user secret, etc. It's just simply verified by trying to decrypt senstive data. Just requires to have some sort of the CRC/hash/etc to validate that the data was decrypted with correct key (or not).

The most likely scenario if when a device are physically lost or stolen, senstive info should be protected from relatively common and simple attacks (i.e. not requiring a lot of investments to crack it) to keep it for some time before user notices that the device are missing and revokes the key.

I've actually already implemented that by storing encrypted sensitive info in my structured representation on flash. Had to refuse from storing it on the encrypted NVS partition as it turns out it serves other purposes. But the NVS encryption API can have a similar behaviour if it adds ability to set the "seed" (aka HMAC key actually) from external data (user input, authorized network host, etc) instead of static magic numbers 0xAEBE5A5A, 0xCEDEA5A5. I was actually expecting that it should have such option.

So that's why I've made this post at first but already figured it out. I would appretiate any comments/reasonable criticism/etc on my plan though.

Thanks!

Re: NVS partition encryption keys in HMAC mode

Posted: Thu Jan 11, 2024 1:01 am
by MicroController
3) It removes the "burden" of secure authention because that effectively provides it by itself. We don't need to store hash of the user secret, etc. It's just simply verified by trying to decrypt senstive data. Just requires to have some sort of the CRC/hash/etc to validate that the data was decrypted with correct key (or not).
You definitely need to protect the device against running malicious firmware (->secure boot). Otherwise an attacker could run his own firmware and let it brute-force the user's secret offline.
And/or better: Don't implement this in a way that would enable anyone to determine if a given secret/password may or may not be correct. I.e. every input should yield an output that cannot be distinguished from a the correct value offline..

Re: NVS partition encryption keys in HMAC mode

Posted: Sat Jan 13, 2024 2:43 am
by NStorm
You definitely need to protect the device against running malicious firmware (->secure boot).
I'll definitely add this as a "layer" of security. I just don't want to rely only on that feature, leaving things behind unprotected.
Otherwise an attacker could run his own firmware and let it brute-force the user's secret offline.
Protection against bruteforce comes from high count of KDF iterations to make it slow enough.
And/or better: Don't implement this in a way that would enable anyone to determine if a given secret/password may or may not be correct. I.e. every input should yield an output that cannot be distinguished from a the correct value offline..
I've actually thought on having that. But that would turn out to be bad user experience.