Skip to content

espflash save-image does not update the App Image SHA256 in the correct location. #715

@chris-subtlebytes

Description

@chris-subtlebytes

When generating a project with secure boot v2, the bootloader is padded: https://docs.espressif.com/projects/esp-idf/en/v5.3.2/esp32c3/security/secure-boot-v2.html#secure-padding

Using a padded image with espflash save-image --bootloader path-to-bootloader ... will read the file into a buffer:

flasher/mod.rs:

impl FlashData {
    pub fn new(...) -> Result<Self, Error> {
        // If the '--bootloader' option is provided, load the binary file at the
        // specified path.
        let bootloader = if let Some(path) = bootloader {
            let data = fs::canonicalize(path)
                .and_then(fs::read)
                .map_err(|e| Error::FileOpenError(path.display().to_string(), e))?;

            Some(data)
        } else {
            None
        };

Then that bootloader buffer gets passed to image_format.rs IdfBootloaderFormat. From there, the header is updated to match the args so the sha256 needs updating. However, the location of the sha256 is naively calculated as being the last 32 bytes of the bootloader file. However, it's actually the last 32 bytes of the bootloader without padding.

image_format.rs:

        // re-calculate hash of the bootloader - needed since we modified the header
        let bootloader_len = bootloader.len(); // <- size of the file including padding
        let mut hasher = Sha256::new();
        hasher.update(&bootloader[..bootloader_len - 32]); //  <-- wrong location of sha256
        let hash = hasher.finalize();
        bootloader.to_mut()[bootloader_len - 32..].copy_from_slice(&hash);

One approach, which I'm not sure is the correct one due to all the different environments but worked in my specific case, was to walk through the bootloader segments while adding up the length. Then adding width the 16-byte aligned 1-byte checksum at the end. Maybe a better approach can be derived by referencing esptool/bin_image.py directly.

https://docs.espressif.com/projects/esp-idf/en/v5.3.2/esp32c3/api-reference/system/app_image_format.html#application-image-structures

The image has a single checksum byte after the last segment. This byte is written on a sixteen byte padded boundary, so the application image might need padding.

Without this, secure-boot-enabled ESPs will not boot because the first-stage bootloader will detect the mismatch between the actual and old unchanged SHA256.

This appears to be related but not the same as #713

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status

    Done

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions