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

Identical app images for Secure Boot and Non-Secure Boot bootloaders (IDFGH-12079) #13139

Closed
jkronaw opened this issue Feb 8, 2024 · 6 comments
Assignees
Labels
Resolution: NA Issue resolution is unavailable Status: Done Issue is done internally Type: Feature Request Feature request for IDF

Comments

@jkronaw
Copy link

jkronaw commented Feb 8, 2024

Secure Boot is great for production, yet an obstacle in development. Given the current ESP-IDF v5.1 features, we can either enable or disable secure boot but this configuration always affects bootloader and app image.

Let's say I flash 10 devices with secure boot off and 10 devices with secure boot on. If I want to update them all, I might try to use a OTA image that is signed with the correct key.

Installing this OTA image on the "secure boot off" bootloader devices works just fine. However, if I install a "secure boot on" OTA update on the "secure boot off" bootloader devices, I get the following error:

E (237) secure_boot: Mismatch in secure boot settings: the app config is enabled but eFuse not

I can live with that error. However, what it implies is that the newly installed "secure boot on" image has lost updatability. It was compiled with the CONFIG_SECURE_BOOT option on and therefore will try to verify new OTA images with secure boot keys from the eFuses. These are not available however since we are running the "secure boot off" bootloader.

Trying an OTA update anyway will fail with the following errors:

E (22822) secure_boot_v2: Could not read secure boot digests!
E (22823) esp_image: Secure boot signature verification failed
W (22849) esp_image: image valid, signature bad

My initial hope of just being able to used signed binaries for all bootloader types is gone now.

Preferred Solution

I would appreciate a mechanism where a signed OTA image can also run on devices with bootloaders that have secure boot disabled. Has this use case never been considered or is there a technical reason why it is impossible?

Current Approach

Without modifying the ESP-IDF source code, the only solution for updating both secure boot and non secure boot devices is by two different OTA variants.

For testing purposes, we are running secure boot and non secure boot variants of our upcoming product in combination. The overhead of providing two update binaries instead of a single one despite functionally equivalent is not negligable in development and testing environments.

@jkronaw jkronaw added the Type: Feature Request Feature request for IDF label Feb 8, 2024
@espressif-bot espressif-bot added the Status: Opened Issue is new label Feb 8, 2024
@github-actions github-actions bot changed the title Identical app images for Secure Boot and Non-Secure Boot bootloaders Identical app images for Secure Boot and Non-Secure Boot bootloaders (IDFGH-12079) Feb 8, 2024
@KonstantinKondrashov
Copy link
Collaborator

Hi @jkronaw!
Yes, we did not consider exactly this use case. The simple&fast solution for this is to make changes in this function https://github.com/espressif/esp-idf/blob/master/components/bootloader_support/src/secure_boot_v2/secure_boot_signatures_app.c#L144-L151 (sure need to check other places).

We will discuss it internally.

If you can try this way, that would be good.
Thanks for sharing your experience.

static esp_err_t get_secure_boot_key_digests(esp_image_sig_public_key_digests_t *public_key_digests)
{
if (!esp_secure_boot_enabled()) {
    // Gets key digests from running app
    ESP_LOGI(TAG, "Take trusted digest key(s) from running app");
    return esp_secure_boot_get_signature_blocks_for_running_app(true, public_key_digests);
} else {
    ESP_LOGI(TAG, "Take trusted digest key(s) from eFuse block(s)");
    // Read key digests from efuse
    esp_secure_boot_key_digests_t efuse_trusted;
    if (esp_secure_boot_read_key_digests(&efuse_trusted) == ESP_OK) {

@jkronaw
Copy link
Author

jkronaw commented Feb 8, 2024

Hi @KonstantinKondrashov,

replacing #ifdef CONFIG_SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT with a call to esp_secure_boot_enabled() solves my problem. Thank you.

I found something else which probably should be addressed in that context. It applies when trying to do a factory reset by using esp_ota_set_boot_partition from the "secure boot on" image which your changes from above and runs on a "secure boot off" bootloader.

const esp_partition_t *pFactory = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
if (pFactory && esp_ota_set_boot_partition(pFactory ) == ESP_OK) {
    // do reboot
}

When calling esp_ota_set_boot_partition(pFactory), I get the following logs.

I (5217) esp_image: Verifying image signature...
I (5218) secure_boot_v2: Take trusted digest key(s) from running app
E (5220) esp_image: Secure boot signature verification failed
I (5220) esp_image: Calculating simple hash to check for corruption...
W (5246) esp_image: image valid, signature bad

Since the factory partition has been flashed along with the "no secure boot" bootloader, it does not have a signature block. When I flash a signed app manually to the factory slot and try the above factory reset again, it works.

I (8551) esp_image: Verifying image signature...
I (8552) secure_boot_v2: Take trusted digest key(s) from running app
I (8554) secure_boot_v2: #0 app key digest == #0 trusted key digest
I (8554) secure_boot_v2: Verifying with RSA-PSS...
I (8596) secure_boot_v2_rsa: Signature verified successfully!

I hope this helps.

@KonstantinKondrashov
Copy link
Collaborator

Hi @jkronaw!
Regarding the issue with esp_ota_set_boot_partition(). Yes, the log is correct, when you are trying to go from the signed running app to the unsigned (factory) app. This function in your case (APP_FACTORY) should just erase the ota_data partition that leads to the bootloader selecting to boot the factory portion. I can suggest using this code instead of calling the esp_ota_set_boot_partition().

Another way would be to introduce a new Kconfig option like CONFIG_SECURE_BOOT_ALLOW_BOOT_UNSIGNED_FACTORY_APP and skip the error if (image_validate(partition, ESP_IMAGE_VERIFY) != ESP_OK) and only for the FACTORY partition. It's not good for me to add a new option that changes the behavior a little.

            const esp_partition_t *find_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, NULL);
            if (find_partition != NULL) {
                return esp_partition_erase_range(find_partition, 0, find_partition->size);
            } else {
                return ESP_ERR_NOT_FOUND;
            }

Let me know if you are ok with using the erase approach in your code.

@jkronaw
Copy link
Author

jkronaw commented Feb 13, 2024

Erasing the OTA partition achieves just the same thing. I tested it, it works. Thanks again, @KonstantinKondrashov.

Will I hear from you about if or when changes regarding my use case will make it into master? BR

@KonstantinKondrashov
Copy link
Collaborator

Hi @jkronaw! I already created the MR in our internal repo, it is reviewing now.
This MR will be closed automatically when it is published here, I guess within a week.
The changes will be for master. I see that you use v5.1. Your use case is not a very common case so we would not want to backport it. Would it be ok for you to cherry-pick the changes for your IDF version?
Thanks.

@jkronaw
Copy link
Author

jkronaw commented Feb 13, 2024

Sure, thanks!

@espressif-bot espressif-bot added Status: In Progress Work is in progress Status: Reviewing Issue is being reviewed Status: Done Issue is done internally Resolution: NA Issue resolution is unavailable and removed Status: Opened Issue is new Status: In Progress Work is in progress Status: Reviewing Issue is being reviewed labels Feb 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Resolution: NA Issue resolution is unavailable Status: Done Issue is done internally Type: Feature Request Feature request for IDF
Projects
None yet
Development

No branches or pull requests

4 participants