mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-05 20:31:01 +00:00
docs(common): add section on reproducible builds
This commit is contained in:
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)
|
||||
|
81
docs/common/reproducible-build.md
Normal file
81
docs/common/reproducible-build.md
Normal file
@ -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)._
|
79
docs/hardware/model-one/firmware-format.md
Normal file
79
docs/hardware/model-one/firmware-format.md
Normal file
@ -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…
Reference in New Issue
Block a user