Skip to content

Add secure padding for save-image #876

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open

Conversation

thetek42
Copy link
Contributor

@thetek42 thetek42 commented Jun 5, 2025

This PR adds support for "secure padding", just like esptool.py's --secure-pad-v2 option, as discussed in #713.

In contrast to main...SergioGasquez:espflash:feat/secure-padding, this also adds an additional padding section, just like esptool.py. Thus, using esptool.py and espflash save-image with --secure-pad-v2 will now result in exactly the same output.

Also, it makes the connection::reset crate public again, since it is required to call the Flasher::connect method when espflash is used as a library. It was previously made pub(crate) in be1ff81.

@SergioGasquez
Copy link
Member

Hi! Here are my testing results.

# Generate the project
> esp-generate --chip=esp32s3 secure-pad (do not select any features)
# Generate the binaries
> esptools tool --chip esp32s3 elf2image --version 2 --secure-pad-v2 --output esptool.bin target/xtensa-esp32s3-none-elf/release/secure-pad
> espflash save-image --secure-pad-v2 --chip esp32s3 target/xtensa-esp32s3-none-elf/release/secure-pad espflash.bin
# Compare binaries
> esptools tool --chip esp32s3 image_info esptool.bin
...
7 segments

Segment 1: len 0x00350 load 0x3c000020 file_offs 0x00000018 [DROM]
Segment 2: len 0x00258 load 0x3fc89168 file_offs 0x00000370 [BYTE_ACCESSIBLE,MEM_INTERNAL,DRAM]
Segment 3: len 0x00400 load 0x40378000 file_offs 0x000005d0 [MEM_INTERNAL,IRAM]
Segment 4: len 0x00d68 load 0x40378400 file_offs 0x000009d8 [MEM_INTERNAL,IRAM]
Segment 5: len 0x0e8c8 load 0x00000000 file_offs 0x00001748 [PADDING]
Segment 6: len 0x013e8 load 0x42010020 file_offs 0x00010018 [IROM]
Segment 7: len 0x0ebc0 load 0x00000000 file_offs 0x00011408 [PADDING]
Checksum: 0e (valid)
Validation Hash: 894bf1f00093738fa4da353d4483d6d14969b0f657ccd6f4e6fff5f4d3342848 (valid)
Status Ok(ExitStatus(unix_wait_status(0)))
> esptools tool --chip esp32s3 image_info espflash.bin
...
6 segments

Segment 1: len 0x00350 load 0x3c000020 file_offs 0x00000018 [DROM]
Segment 2: len 0x00258 load 0x3fc89168 file_offs 0x00000370 [BYTE_ACCESSIBLE,MEM_INTERNAL,DRAM]
Segment 3: len 0x01168 load 0x40378000 file_offs 0x000005d0 [MEM_INTERNAL,IRAM]
Segment 4: len 0x0e8d0 load 0x00000000 file_offs 0x00001740 [PADDING]
Segment 5: len 0x013e8 load 0x42010020 file_offs 0x00010018 [IROM]
Segment 6: len 0x0ebc0 load 0x00000000 file_offs 0x00011408 [PADDING]
Checksum: 0e (valid)
Validation Hash: 865e0745cc17b5ea9c8e7e2e499e5f4cc6a774c490c286013aed72fa6ec11ebc (valid)
Status Ok(ExitStatus(unix_wait_status(0)))

cc @ivmarkov

@thetek42
Copy link
Contributor Author

thetek42 commented Jul 7, 2025

Rebased in order to adapt to changes on main.

Since the issue with the connection::reset module being required in order to call Flasher::connect has been resolved on main, it is no longer necessary for the module to be public, so I made it private again.

@thetek42
Copy link
Contributor Author

thetek42 commented Aug 7, 2025

@SergioGasquez In case you are wondering, there are a few different reasons for why the .bin file you got from esptool.py and espflash are different.

  1. For some reason, esp-hal projects have two IRAM segments when generating the .bin with esptool.py:
    Segment 3: len 0x00400 load 0x40378000 file_offs 0x000005d0 [MEM_INTERNAL,IRAM]
    Segment 4: len 0x00d68 load 0x40378400 file_offs 0x000009d8 [MEM_INTERNAL,IRAM]
    
    Whereas when you generate it with espflash, it is merged into one IRAM segment:
    Segment 3: len 0x01168 load 0x40378000 file_offs 0x000005d0 [MEM_INTERNAL,IRAM]
    
    To check: 0x400 + 0xd68 (the sized of the two IRAM segments by esptool) = 0x1168 (the size of the one IRAM segment by espflash). I do not know why espflash merges the two segments and esptool does not.
  2. You need to use precisely the same settings for generating the .bin file, e.g.:
    esptool.py --chip esp32 elf2image \
        --flash_mode qio \
        --flash_freq 40m \
        --flash_size 16MB \
        --max-rev-full 399 \
        --secure-pad-v2 \
        --output esptool.bin target/xtensa-esp32-espidf/debug/secure-pad
    espflash save-image --chip esp32 \
        --flash-mode qio \
        --flash-freq 40mhz \
        --flash-size 16mb \
        --secure-pad-v2 \
        target/xtensa-esp32-espidf/debug/secure-pad espflash.bin
    
  3. espflash does not fill the "app_elf_sha256" field of the app descriptor, and leaves it zeroed, whereas esptool does fill this field. If you want to prevent esptool filling the ELF SHA256 field, you can use the --elf-sha256-offset 9999999999 flag. It instructs esptool to move the checksum to an invalid position, which causes esptool to not write the checksum in the first place. It's kinda hacky, but it works.

If you do all of those three steps (Not using esp-hal and relying on ESP-IDF instead, using exactly the same parameters and preventing the inclusion of a proper ELF checksum), you will actually get exactly the same binary:

$ esptool.py --chip esp32 elf2image --flash_mode qio --flash_freq 40m --flash_size 16MB --max-rev-full 399 --secure-pad-v2 --elf-sha256-offset 9999999999 --output esptool.bin target/xtensa-esp32-espidf/debug/secure-pad
$ espflash save-image --chip esp32 --flash-mode qio --flash-freq 40mhz --flash-size 16mb --secure-pad-v2 target/xtensa-esp32-espidf/debug/secure-pad espflash.bin
$ esptool.py --chip esp32 image_info espflash.bin
    esptool.py v4.9.1
    File size: 589824 (bytes)
    Image version: 1
    Entry point: 400818a4
    6 segments

    Segment 1: len 0x2817c load 0x3f400020 file_offs 0x00000018 [DROM]
    Segment 2: len 0x026a8 load 0x3ffb0000 file_offs 0x0002819c [BYTE_ACCESSIBLE,DRAM]
    Segment 3: len 0x057c4 load 0x40080000 file_offs 0x0002a84c [IRAM]
    Segment 4: len 0x4eb70 load 0x400d0020 file_offs 0x00030018 [IROM]
    Segment 5: len 0x05e0c load 0x400857c4 file_offs 0x0007eb90 [IRAM]
    Segment 6: len 0x0b624 load 0x00000000 file_offs 0x000849a4 [PADDING]
    Checksum: 78 (valid)
    Validation Hash: 4f44f71bdf093fbc6dbff85a9c546c6dab8912d542620c8dd3ff047ad90f379b (valid)
$ esptool.py --chip esp32 image_info esptool.bin 
    esptool.py v4.9.1
    File size: 589824 (bytes)
    Image version: 1
    Entry point: 400818a4
    6 segments
    
    Segment 1: len 0x2817c load 0x3f400020 file_offs 0x00000018 [DROM]
    Segment 2: len 0x026a8 load 0x3ffb0000 file_offs 0x0002819c [BYTE_ACCESSIBLE,DRAM]
    Segment 3: len 0x057c4 load 0x40080000 file_offs 0x0002a84c [IRAM]
    Segment 4: len 0x4eb70 load 0x400d0020 file_offs 0x00030018 [IROM]
    Segment 5: len 0x05e0c load 0x400857c4 file_offs 0x0007eb90 [IRAM]
    Segment 6: len 0x0b624 load 0x00000000 file_offs 0x000849a4 [PADDING]
    Checksum: 78 (valid)
    Validation Hash: 4f44f71bdf093fbc6dbff85a9c546c6dab8912d542620c8dd3ff047ad90f379b (valid)
$ hexdump espflash.bin > espflash.hex
$ hexdump esptool.bin > esptool.hex  
$ diff espflash.hex esptool.hex -s
    Files espflash.hex and esptool.hex are identical

@SergioGasquez
Copy link
Member

Thanks for the details! We are currently evaluating doing a patch release with #932 #931. As this PR has breaking changes, Ill come back to it once we made a decision regarding the patch release. Changes LGTM though, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants