docs(common): add section on reproducible builds

pull/1924/head
Martin Milata 2 years ago
parent 221977ad9d
commit 345c90ccba

@ -21,12 +21,14 @@
- [Boot stages](hardware/model-t/boot.md)
- [Memory layout](hardware/model-t/memory.md)
- [Model One](hardware/model-one/index.md)
- [Firmware format](hardware/model-one/firmware-format.md)
- [Common](common/index.md)
- [Communication](common/communication/index.md)
- [Sessions](common/communication/sessions.md)
- [Passphrase](common/communication/passphrase.md)
- [Migration](common/communication/passphrase-redesign-migration.md)
- [Bitcoin signing](common/communication/bitcoin-signing.md)
- [Reproducible builds](common/reproducible-build.md)
- [Storage](storage/index.md)
- [Tests](tests/index.md)
- [Device Tests](tests/device-tests.md)

@ -0,0 +1,81 @@
# Reproducible build
We want to invite the wider community to participate in the verification of
the firmware built by SatoshiLabs. With reasonable effort you should be able to
build the firmware and verify that it's identical to the official firmware.
Trezor Firmware uses [Nix](https://nixos.org/), [Poetry](https://python-poetry.org/)
and [Cargo](https://doc.rust-lang.org/cargo/) to make the build environment
deterministic. We also provide a Docker-based script so that the build can be
performed with a single command on usual x86 Linux system.
## Building
First you need to determine which *version tag* you want to build:
* for Trezor One it is `legacy/vX.Y.Z`, e.g. `legacy/v1.10.3`,
* for Trezor Model T it is `core/vX.Y.Z`, e.g. `core/v2.4.2`.
Assuming you want to build `core/v2.4.2`:
1. install [Docker](https://www.docker.com/)
2. clone the firmware repository: `git clone https://github.com/trezor/trezor-firmware.git`
3. go into the firmware directory: `cd trezor-firmware`
4. checkout the version tag: `git checkout core/v2.4.2`
5. run: `bash build-docker.sh core/v2.4.2`
After the build finishes the firmware images are located in:
* `build/legacy/firmware/firmware.bin` and `build/legacy-bitcoinonly/firmware/firmware.bin` for Trezor One,
* `build/core/firmware/firmware.bin` and `build/core-bitcoinonly/firmware/firmware.bin` for Trezor Model T.
## Verifying
The result won't be bit-by-bit identical with the official images because the
official images are signed while local builds aren't. Official release of
Trezor One firmware also has additional 256-byte legacy header that needs to be
removed first.
### Trezor T
The [firmware header](../hardware/model-t/boot.md#firmware-header) contains 65
bytes of signature data at offset 0x15bf. After overwriting it by zeros in
official release the binaries should become identical.
```
wget https://data.trezor.io/firmware/2/trezor-2.4.2.bin
# the following line removes 65 bytes of signature data from the official firmware
dd if=/dev/zero of=trezor-2.4.2.bin bs=1 seek=5567 count=65 conv=notrunc
# the following two lines print out the hashes of the firmwares
sha256sum trezor-2.4.2.bin
sha256sum build/core/firmware/firmware.bin
```
### Trezor One
Official T1 firmware starts with [256-byte legacy header](../hardware/model-one/firmware-format.md)
used for compatibility with old bootloaders. Locally built firmware doesn't have this header.
```
wget https://data.trezor.io/firmware/1/trezor-1.10.3.bin
# strip legacy header
tail -c +257 trezor-1.10.3.bin > trezor-1.10.3-nolegacyhdr.bin
```
The [v2 header](../hardware/model-one/firmware-format.md#v2-header) has 3x65
bytes of signature data at offset 0x220. Overwrite by zeros to obtain image
identical to the one built locally.
```
dd if=/dev/zero of=trezor-1.10.3-nolegacyhdr.bin bs=1 seek=544 count=195 conv=notrunc
sha256sum trezor-1.10.3-nolegacyhdr.bin
sha256sum build/legacy/firmware/firmware.bin
```
_Note: Fingerprints displayed for T1 at the end of `build-docker.sh` do not match fingerprints of
official firmware due to the legacy header._
_Note: T1 firmware built this way won't boot because unsigned firmware needs to be built with
[`MEMORY_PROTECT=0`](../legacy/index.md#combining-bootloader-and-firmware-with-various-memory_protect-settings-signedunsigned)._

@ -0,0 +1,79 @@
# Trezor One firmware format
Historically Trezor One has been using 256-byte header (w/ `TRZR` magic string) followed by the
actual firmware. Since version 1.8.0, different 1024-byte header (w/ `TRZF` magic string) is in use,
and building firmware from this repository produces firmware image containing such header followed
by firmware code.
Official release firmware contains both these headers for compatibility with old bootloaders. That
means there is a 256-byte `TRZR` header followed by 1024-byte `TRZF` header followed by code.
* Hash function used for computing data digest for signatures is SHA256.
* Signature system is ECDSA over SECP256k1.
* All multibyte integer values are little endian.
## Legacy Header
Total length of legacy header is always 256 bytes.
| offset | length | name | description |
|-------:|-------:|------|-------------|
| 0x0000 | 4 | magic | firmware magic `TRZR` |
| 0x0004 | 4 | codelen | length of V2 header + code (length of code before 1.8.0) |
| 0x0008 | 1 | sigindex1 | index of key for `sig1` |
| 0x0009 | 1 | sigindex2 | index of key for `sig2` |
| 0x000A | 1 | sigindex3 | index of key for `sig3` |
| 0x000B | 1 | flags | unused since 1.8.0 (zeroed) |
| 0x000C | 52 | reserved | not used yet (zeroed) |
| 0x0040 | 64 | sig1 | signature #1 |
| 0x0080 | 64 | sig2 | signature #2 |
| 0x00C0 | 64 | sig3 | signature #3 |
Signature verification:
* Calculate SHA256 digest of firmware without this header.
* Verify signature `sig1` of the digest against public key with index `sigindex1` in [`V1_BOOTLOADER_KEYS`](../../../python/src/trezorlib/firmware.py).
* Repeat for `sig2` and `sig3`. Indexes must be distinct.
## V2 Header
This header has the same format as [Model T Firmware Header](../model-t/boot.md#firmware-header),
however due to different signature scheme the `sigmask` and `sig` fields are zeroed and part of the
reserved space is used for T1-specific fields `sig1`-`sig3`, `sigindex1-sigindex3`. Total length of
v2 header is always 1024 bytes.
| offset | length | name | description |
|-------:|-------:|------|-------------|
| 0x0000 | 4 | magic | firmware magic `TRZF` |
| 0x0004 | 4 | hdrlen | length of the firmware header |
| 0x0008 | 4 | expiry | valid until timestamp (0=infinity) |
| 0x000C | 4 | codelen | length of the firmware code (without the header) |
| 0x0010 | 1 | vmajor | version (major) |
| 0x0011 | 1 | vminor | version (minor) |
| 0x0012 | 1 | vpatch | version (patch) |
| 0x0013 | 1 | vbuild | version (build) |
| 0x0014 | 1 | fix_vmajor | version of last critical bugfix (major) |
| 0x0015 | 1 | fix_vminor | version of last critical bugfix (minor) |
| 0x0016 | 1 | fix_vpatch | version of last critical bugfix (patch) |
| 0x0017 | 1 | fix_vbuild | version of last critical bugfix (build) |
| 0x0018 | 8 | reserved | not used yet (zeroed) |
| 0x0020 | 32 | hash1 | hash of the first code chunk excluding both the legacy and the v2 header (129792 B) |
| 0x0040 | 32 | hash2 | hash of the second code chunk (128 KiB), zeroed if unused |
| ... | ... | ... | ... |
| 0x0200 | 32 | hash16 | hash of the last possible code chunk (128 KiB), zeroed if unused |
| 0x0220 | 64 | sig1 | signature #1 |
| 0x0260 | 64 | sig2 | signature #2 |
| 0x02A0 | 64 | sig3 | signature #3 |
| 0x02E0 | 1 | sigindex1 | index of key for `sig1` |
| 0x02E1 | 1 | sigindex2 | index of key for `sig2` |
| 0x02E2 | 1 | sigindex3 | index of key for `sig3` |
| 0x02E3 | 220 | reserved | not used yet (zeroed) |
| 0x03BF | 1 | reserved_sigmask | unused in T1 (zeroed) |
| 0x03C0 | 64 | reserved_sig | unused in T1 (zeroed) |
Signature verification:
* Calculate SHA256 digest of the entire header with `sig1`-`sig3` and `sigindex1`-`sigindex3` zeroed
out.
* Verify signature `sig1` of the digest against public key with index `sigindex1` in [`V1_BOOTLOADER_KEYS`](../../../python/src/trezorlib/firmware.py).
* Repeat for `sig2` and `sig3`. Indexes must be distinct.
Loading…
Cancel
Save