Reproducible esp32c3 firmware with Rust, repro-env and Arch Linux

kpcyrd kpcyrd at archlinux.org
Thu Nov 30 01:45:33 UTC 2023


hello list,

I recently did some firmware development in Rust, and since this topic 
was raised during the summit I challenged myself to get the firmware 
build to be reproducible.

I choose the esp32c3 because it has good Rust support from the esp-rs 
project[1], and you can get a dev board for about 6-8€. To document my 
build environment I used repro-env[2] together with Arch Linux because 
its archive[3] is very reliable and contains all the different Rust 
development tools I needed.

[1]: https://github.com/esp-rs
[2]: https://github.com/kpcyrd/repro-env
[3]: https://archive.archlinux.org/

Since Rust is fairly reproducible by default I expected this to be 
somewhat straight forward, and indeed there were only two issues I had 
to trouble-shoot:

- The build time was embedded into the firmware
- The output of `git describe --always --tags --dirty` was also embedded

For the first one I found a configuration option quickly, but the later 
was an issue because the call was coming from a dependency, but 
inspecting the .git/ directory of my firmware project. This folder is 
considered a build input and not normalized by repro-env, on my local 
machine this folder contains the full git history with all tags, on 
github actions I used actions/checkout at v3 which only checks out a single 
commit by default. Because of this it would embed `v0.1.0-2-g15f33c4` in 
one firmware file, and `15f33c4` in the other.

I fixed both by adding this to my sdkconfig.defaults:

CONFIG_APP_REPRODUCIBLE_BUILD=y
CONFIG_APP_EXCLUDE_PROJECT_VER_VAR=y

The first one disables the embedded time, and the second one disables 
PROJECT_VER, which is the variable name for the `git describe` output 
(likely coming from esp-idf-sys's cmake).

If you want to try this for yourself, the following should work for you:

```
git clone https://github.com/kpcyrd/d3xs
cd d3xs
git checkout 06d72b02c7ce56769f5cd5f6e473b06f6c7a33bd
repro-env build -- sh -c '
     D3XS_DOOR_KEY="w/CSnPJnWTaEIYpEvXvF+ktwh236iSDZfSx6hExB4bM=" \
     D3XS_BRIDGE_KEY="cW49lkXDeM0wOT8N7QxAWePmWs8xZK1FXt1uQT/pcG4=" \
     make firmware'
```

This should give you a binary at 
./target-firmware/riscv32imc-esp-espidf/release/d3xs-firmware with the 
following sha256:

089ac1781291463da02dece7a18d69454be41e1f727a6c10b5924bc3966e8178

This hash is also currently visible in the github actions output[4], my 
repository however does not host pre-compiled binaries: The firmware 
implements a challenge/response protocol and the binaries contain 
embedded keys that are supposed to be kept secret (this is the reason 
you need to set these environment variables). Similiar to coreboot, 
users are expected to build their own firmware (with their own keys). It 
is, however, a non-trivial, real world firmware that is expected to 
produce reproducible binaries.

[4]: https://github.com/kpcyrd/d3xs/actions/runs/7039665873/job/19159115908

---

Having said that, this project depends on the esp-idf-sys crate, which 
is not fully self-contained[5]. The github.com/espressif/esp-idf git 
revision is selected by tag name instead of a pseudo-cryptographic sha1 
hash. There are also two other issues[6][7] with people trying to 
package all necessary resources with Nix (but no success so far).

If somebody gets this to work in an offline nix build (using only 
content-addressed nix inputs) please let me know.

[5]: https://github.com/esp-rs/esp-idf-sys/issues/132
[6]: https://github.com/esp-rs/esp-idf-sys/issues/184
[7]: https://github.com/esp-rs/embuild/issues/81

Anyway -

Slay always,
kpcyrd


More information about the rb-general mailing list