Trezor Core Boot Stages

Trezor T initialization is split into two stages. See Memory Layout for info about in which sectors each stage is stored.

First stage (boardloader) is stored in write-protected area, which means it is non-upgradable. Only second stage (bootloader) update is allowed.

First Stage - Boardloader

First stage checks the integrity and signatures of the second stage and runs it if everything is OK.

If first stage boardloader finds a valid second stage bootloader image on the SD card (in raw format, no filesystem), it will replace the internal second stage, allowing a second stage update via SD card.

The boardloader is special in that it is the device's write protected embedded code. The primary purpose for write protecting the boardloader is to make it the immutable portion that can defend against code-based attacks (e.g.- BadUSB) and bugs that would reprogram any/all of the embedded code. It assures that only verified signed embedded code is run on the device (and that the intended code is run, and not skipped). The write protection also provides some defense against attacks where the attacker has physical control of the device.

The boardloader must include an update mechanism for later stage code because if it did not, then a corruption/erasure of later stage flash memory would leave the device unusable (only the boardloader could run and it would not pass execution to a later stage that fails signature validation).

Developer note:

A microSD card can be prepared with the following. Note that the bootloader is allocated 128 KiB.

WARNING: Ensure that you want to overwrite and destroy the contents of /dev/mmcblk0 before running these commands. Likewise, /dev/mmcblk0 may be replaced by your own specific destination.

  1. sudo dd if=/dev/zero of=/dev/mmcblk0 bs=512 count=256 conv=fsync

  2. sudo dd if=build/bootloader/bootloader.bin of=/dev/mmcblk0 bs=512 conv=fsync

Second Stage - Bootloader

Second stage checks the integrity and signatures of the firmware and runs it if everything is OK.

If second stage bootloader detects a pressed finger on the display or there is no firmware loaded in the device, it will start in a firmware update mode, allowing a firmware update via USB.

Common notes

  • Hash function used for computing data digest for signatures is BLAKE2s.
  • Signature system is Ed25519 (allows combining signatures by multiple keys into one).
  • All multibyte integer values are little endian.
  • There is a tool called headertool.py which checks validity of the bootloader/firmware images including their headers.

Bootloader Format

Trezor Core (second stage) bootloader consists of 2 parts:

  1. bootloader header
  2. bootloader code

Bootloader Header

Total length of bootloader header is always 1024 bytes.

offsetlengthnamedescription
0x00004magicfirmware magic TRZB
0x00044hdrlenlength of the bootloader header
0x00084expiryvalid until timestamp (0=infinity)
0x000C4codelenlength of the bootloader code (without the header)
0x00101vmajorversion (major)
0x00111vminorversion (minor)
0x00121vpatchversion (patch)
0x00131vbuildversion (build)
0x00141fix_vmajorversion of last critical bugfix (major)
0x00151fix_vminorversion of last critical bugfix (minor)
0x00161fix_vpatchversion of last critical bugfix (patch)
0x00171fix_vbuildversion of last critical bugfix (build)
0x00188reservednot used yet (zeroed)
0x002032hash1hash of the first code chunk (128 - 1 KiB), this excludes the header
0x004032hash2hash of the second code chunk (128 KiB), zeroed if unused
............
0x020032hash16hash of the last possible code chunk (128 KiB), zeroed if unused
0x0220415reservednot used yet (zeroed)
0x03BF1sigmaskSatoshiLabs signature indexes (bitmap)
0x03C064sigSatoshiLabs aggregated signature of the bootloader header

Firmware Format

Trezor Core firmware consists of 3 parts:

  1. vendor header
  2. firmware header
  3. firmware code

Vendor Header

Total length of vendor header is 84 + 32 * (number of pubkeys) + (length of vendor string rounded up to multiple of 4) + (length of vendor image) bytes rounded up to the closest multiple of 512 bytes.

offsetlengthnamedescription
0x00004magicfirmware magic TRZV
0x00044hdrlenlength of the vendor header (multiple of 512)
0x00084expiryvalid until timestamp (0=infinity)
0x000C1vmajorversion (major)
0x000D1vminorversion (minor)
0x000E1vsig_mnumber of signatures needed to run the firmware from this vendor
0x000F1vsig_nnumber of different pubkeys vendor provides for signing
0x00102vtrustlevel of vendor trust (bitmap)
0x001214reservednot used yet (zeroed)
0x002032vpub1vendor pubkey 1
............
?32vpubnvendor pubkey n
?1vstr_lenvendor string length
??vstrvendor string
??vstrpadpadding to a multiple of 4 bytes
??vimgvendor image (120x120 pixels in TOIf format)
??reservedpadding to an address that is -65 modulo 512 (zeroed)
?1sigmaskSatoshiLabs signature indexes (bitmap)
?64sigSatoshiLabs aggregated signature of the vendor header

Vendor Trust

Vendor trust is stored as bitmap where unset bit means the feature is active.

bithexmeaning
00x0001wait 1 second
10x0002wait 2 seconds
20x0004wait 4 seconds
30x0008wait 8 seconds
40x0010use red background instead of black one
50x0020require user click
60x0040show vendor string (not just the logo)

Firmware Header

Total length of firmware header is always 1024 bytes.

offsetlengthnamedescription
0x00004magicfirmware magic TRZF
0x00044hdrlenlength of the firmware header
0x00084expiryvalid until timestamp (0=infinity)
0x000C4codelenlength of the firmware code (without the header)
0x00101vmajorversion (major)
0x00111vminorversion (minor)
0x00121vpatchversion (patch)
0x00131vbuildversion (build)
0x00141fix_vmajorversion of last critical bugfix (major)
0x00151fix_vminorversion of last critical bugfix (minor)
0x00161fix_vpatchversion of last critical bugfix (patch)
0x00171fix_vbuildversion of last critical bugfix (build)
0x00188reservednot used yet (zeroed)
0x002032hash1hash of the first code chunk excluding both the firmware and the vendor header (128 - 1 - [vendor header length] KiB)
0x004032hash2hash of the second code chunk (128 KiB), zeroed if unused
............
0x020032hash16hash of the last possible code chunk (128 KiB), zeroed if unused
0x0220415reservednot used yet (zeroed)
0x03BF1sigmaskvendor signature indexes (bitmap)
0x03C064sigvendor aggregated signature of the firmware header