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