1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-22 23:48:12 +00:00

bootloader: cleanup, more documentation, added scripts for checking

This commit is contained in:
Pavol Rusnak 2017-02-08 16:36:19 +01:00
parent 4c8d3e4a35
commit 2d7c6b0c34
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D
13 changed files with 128 additions and 98 deletions

View File

@ -13,7 +13,7 @@ vendor: ## update git submodules
git submodule update --init git submodule update --init
res: ## update resources res: ## update resources
./tools/res_collect.py ./tools/res_collect
build: build_stmhal build_unix build_cross ## build stmhal, unix and mpy-cross micropython ports build: build_stmhal build_unix build_cross ## build stmhal, unix and mpy-cross micropython ports

View File

@ -1,15 +1,26 @@
#TREZOR Core Bootloader #TREZOR Core Bootloader
Bootloader code is split into two stages. See [Memory Layout](memory.md) for more info about in which sectors the code is stored. Bootloader is split into two stages. See [Memory Layout](memory.md) for info about in which sectors each stage is stored.
First stage checks the integrity and signatures of second stage and runs it if everything is OK. First stage is stored in write-protected area, which means it is non-upgradable. Only second stage bootloader update is allowed.
However, if first stage bootloader finds a valid second stage bootloader on the SD card (in raw format, no filesystem), it will replace the internal second stage, allowing an upgrade of the second stage.
First stage is stored in write-protected area, which means only upgrade of the second stage bootloader is allowed. ##First Stage Bootloader
First stage checks the integrity and signatures of the second stage and runs it if everything is OK.
If first stage bootloader 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.
##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 ##Common notes
* Hash function used is SHA-256 and signature system is Ed25519 (allows combining signatures by multiple keys into one). * Hash function used below is SHA-256 and signature system is Ed25519 (allows combining signatures by multiple keys into one).
* All multibyte integer values are little endian. * All multibyte integer values are little endian.
##Bootloader Format ##Bootloader Format
@ -19,6 +30,8 @@ TREZOR Core (second stage) bootloader consists of 2 parts:
1. bootloader header 1. bootloader header
2. bootloader code 2. bootloader code
There is a tool called [check_bootloader](../tools/check_bootloader) which parses and checks validity of the bootloader including its header.
###Bootloader Header ###Bootloader Header
Total length of bootloader header is 256 bytes. Total length of bootloader header is 256 bytes.
@ -26,16 +39,16 @@ Total length of bootloader header is 256 bytes.
| offset | length | name | description | | offset | length | name | description |
|-------:|-------:|------|-------------| |-------:|-------:|------|-------------|
| 0x0000 | 4 | magic | firmware magic `TRZB` | | 0x0000 | 4 | magic | firmware magic `TRZB` |
| 0x0004 | 4 | hlen | length of the bootloader header | | 0x0004 | 4 | hdrlen | length of the bootloader header |
| 0x0008 | 4 | expiry | valid until timestamp (0=infinity) | | 0x0008 | 4 | expiry | valid until timestamp (0=infinity) |
| 0x000C | 4 | codelen | length of the bootloader code | | 0x000C | 4 | codelen | length of the bootloader code (without the header) |
| 0x0010 | 1 | vmajor | version (major) | | 0x0010 | 1 | vmajor | version (major) |
| 0x0011 | 1 | vminor | version (minor) | | 0x0011 | 1 | vminor | version (minor) |
| 0x0012 | 1 | vpatch | version (patch) | | 0x0012 | 1 | vpatch | version (patch) |
| 0x0013 | 1 | vbuild | version (build) | | 0x0013 | 1 | vbuild | version (build) |
| 0x0014 | 1 | slsigidx | SatoshiLabs signature indexes (bitmap) | | 0x0014 | 1 | sigidx | SatoshiLabs signature indexes (bitmap) |
| 0x0015 | 64 | slsig | SatoshiLabs signature | | 0x0015 | 64 | sig | SatoshiLabs signature |
| 0x0079 | 135 | reserved | not used yet | | 0x0055 | 171 | reserved | not used yet (zeroed) |
##Firmware Format ##Firmware Format
@ -45,6 +58,8 @@ TREZOR Core firmware consists of 3 parts:
2. firmware header 2. firmware header
3. firmware code 3. firmware code
There is a tool called [check_firmware](../tools/check_firmware) which parses and checks validity of the firmware including the both headers.
###Vendor Header ###Vendor Header
Total length of vendor header is 82 + 32 * (number of pubkeys) + (length of vendor string) + (length of vendor image) bytes rounded up to the closest multiply of 256 bytes. Total length of vendor header is 82 + 32 * (number of pubkeys) + (length of vendor string) + (length of vendor image) bytes rounded up to the closest multiply of 256 bytes.
@ -52,19 +67,19 @@ Total length of vendor header is 82 + 32 * (number of pubkeys) + (length of vend
| offset | length | name | description | | offset | length | name | description |
|-------:|-------:|------|-------------| |-------:|-------:|------|-------------|
| 0x0000 | 4 | magic | firmware magic `TRZV` | | 0x0000 | 4 | magic | firmware magic `TRZV` |
| 0x0004 | 4 | hlen | length of the vendor header | | 0x0004 | 4 | hdrlen | length of the vendor header |
| 0x0008 | 4 | expiry | valid until timestamp (0=infinity) | | 0x0008 | 4 | expiry | valid until timestamp (0=infinity) |
| 0x000C | 1 | vsig_m | number of signatures needed to run the firmware from this vendor | | 0x000C | 1 | vsig_m | number of signatures needed to run the firmware from this vendor |
| 0x000D | 1 | vsig_n | number of pubkeys vendor wants to use for signing | | 0x000D | 1 | vsig_n | number of pubkeys vendor wants to use for signing |
| 0x000E | 2 | reserved | not used yet | | 0x000E | 2 | reserved | not used yet (zeroed) |
| 0x0010 | 32 | vpub1 | vendor pubkey 1 | | 0x0010 | 32 | vpub1 | vendor pubkey 1 |
| ... | ... | ... | ... | | ... | ... | ... | ... |
| ? | 32 | vpubn | vendor pubkey n | | ? | 32 | vpubn | vendor pubkey n |
| ? | 1 | vstr_len | vendor string length | | ? | 1 | vstr_len | vendor string length |
| ? | ? | vstr | vendor string | | ? | ? | vstr | vendor string |
| ? | ? | vimg | vendor image (in [TOIf format](toif.md)) | | ? | ? | vimg | vendor image (in [TOIf format](toif.md)) |
| ? | 1 | slsigidx | SatoshiLabs signature indexes (bitmap) | | ? | 1 | sigidx | SatoshiLabs signature indexes (bitmap) |
| ? | 64 | slsig | SatoshiLabs signature | | ? | 64 | sig | SatoshiLabs signature |
###Firmware Header ###Firmware Header
@ -73,16 +88,16 @@ Total length of firmware header is 256 bytes.
| offset | length | name | description | | offset | length | name | description |
|-------:|-------:|------|-------------| |-------:|-------:|------|-------------|
| 0x0000 | 4 | magic | firmware magic `TRZF` | | 0x0000 | 4 | magic | firmware magic `TRZF` |
| 0x0004 | 4 | hlen | length of the firmware header | | 0x0004 | 4 | hdrlen | length of the firmware header |
| 0x0008 | 4 | expiry | valid until timestamp (0=infinity) | | 0x0008 | 4 | expiry | valid until timestamp (0=infinity) |
| 0x000C | 4 | codelen | length of the firmware code | | 0x000C | 4 | codelen | length of the firmware code (without the header) |
| 0x0010 | 1 | vmajor | version (major) | | 0x0010 | 1 | vmajor | version (major) |
| 0x0011 | 1 | vminor | version (minor) | | 0x0011 | 1 | vminor | version (minor) |
| 0x0012 | 1 | vpatch | version (patch) | | 0x0012 | 1 | vpatch | version (patch) |
| 0x0013 | 1 | vbuild | version (build) | | 0x0013 | 1 | vbuild | version (build) |
| 0x0014 | 1 | vndsigidx | vendor signature indexes (bitmap) | | 0x0014 | 1 | sigidx | vendor signature indexes (bitmap) |
| 0x0015 | 64 | vndsig | vendor signature | | 0x0015 | 64 | sig | vendor signature |
| 0x0079 | 135 | reserved | not used yet | | 0x0055 | 171 | reserved | not used yet (zeroed) |
##Various ideas ##Various ideas

View File

@ -4,18 +4,18 @@
| sector | range | size | function | sector | range | size | function
|-----------|-------------------------|--------:|---------------------- |-----------|-------------------------|--------:|----------------------
| Sector 0 | 0x08000000 - 0x08003FFF | 16 KiB | bootloader 1st stage | Sector 0 | 0x08000000 - 0x08003FFF | 16 KiB | bootloader 1st stage (write-protected)
| Sector 1 | 0x08004000 - 0x08007FFF | 16 KiB | storage area | Sector 1 | 0x08004000 - 0x08007FFF | 16 KiB | bootloader 1st stage (write-protected)
| Sector 2 | 0x08008000 - 0x0800BFFF | 16 KiB | storage area | Sector 2 | 0x08008000 - 0x0800BFFF | 16 KiB | storage area
| Sector 3 | 0x0800C000 - 0x0800FFFF | 16 KiB | storage area | Sector 3 | 0x0800C000 - 0x0800FFFF | 16 KiB | storage area
| Sector 4 | 0x08010000 - 0x0801FFFF | 64 KiB | bootloader 2nd stage | Sector 4 | 0x08010000 - 0x0801FFFF | 64 KiB | bootloader 2nd stage
| Sector 5 | 0x08020000 - 0x0803FFFF | 128 KiB | application code | Sector 5 | 0x08020000 - 0x0803FFFF | 128 KiB | firmware
| Sector 6 | 0x08040000 - 0x0805FFFF | 128 KiB | application code | Sector 6 | 0x08040000 - 0x0805FFFF | 128 KiB | firmware
| Sector 7 | 0x08060000 - 0x0807FFFF | 128 KiB | application code | Sector 7 | 0x08060000 - 0x0807FFFF | 128 KiB | firmware
| Sector 8 | 0x08080000 - 0x0809FFFF | 128 KiB | application code | Sector 8 | 0x08080000 - 0x0809FFFF | 128 KiB | firmware
| Sector 9 | 0x080A0000 - 0x080BFFFF | 128 KiB | application code | Sector 9 | 0x080A0000 - 0x080BFFFF | 128 KiB | firmware
| Sector 10 | 0x080C0000 - 0x080DFFFF | 128 KiB | application code | Sector 10 | 0x080C0000 - 0x080DFFFF | 128 KiB | firmware
| Sector 11 | 0x080E0000 - 0x080FFFFF | 128 KiB | application code | Sector 11 | 0x080E0000 - 0x080FFFFF | 128 KiB | firmware
##RAM ##RAM

View File

@ -80,6 +80,7 @@ SRC_LIB = $(addprefix lib/,\
) )
SRC_C = \ SRC_C = \
bootloader/basic.c \
bootloader/bootloader.c \ bootloader/bootloader.c \
bootloader/crypto.c \ bootloader/crypto.c \
bootloader/ui.c \ bootloader/ui.c \

View File

@ -0,0 +1,26 @@
#include STM32_HAL_H
// ### from main.c
void __attribute__((noreturn)) __fatal_error(const char *msg) {
for (volatile uint32_t delay = 0; delay < 10000000; delay++) {
}
// TODO: printf("FATAL ERROR: %s\n", msg);
for (;;) {
__WFI();
}
}
void nlr_jump_fail(void *val) {
__fatal_error("FATAL: uncaught exception");
}
// ### from stm32_it.c
void SysTick_Handler(void) {
extern uint32_t uwTick;
uwTick += 1;
SysTick->CTRL;
}
// ###

View File

@ -2,75 +2,10 @@
#include "crypto.h" #include "crypto.h"
#include "ui.h" #include "ui.h"
#include "display.h" #include "display.h"
// ### from main.c
void flash_error(int n) {
for (int i = 0; i < n; i++) {
// blink(on)
HAL_Delay(250);
// blink(off)
HAL_Delay(250);
}
}
void __attribute__((noreturn)) __fatal_error(const char *msg) {
for (volatile uint32_t delay = 0; delay < 10000000; delay++) {
}
// TODO: printf("FATAL ERROR: %s\n", msg);
for (;;) {
__WFI();
}
}
void nlr_jump_fail(void *val) {
__fatal_error("FATAL: uncaught exception");
}
void SystemClock_Config(void); void SystemClock_Config(void);
// ### from stm32_it.c
/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
void SysTick_Handler(void) {
// Instead of calling HAL_IncTick we do the increment here of the counter.
// This is purely for efficiency, since SysTick is called 1000 times per
// second at the highest interrupt priority.
// Note: we don't need uwTick to be declared volatile here because this is
// the only place where it can be modified, and the code is more efficient
// without the volatile specifier.
extern uint32_t uwTick;
uwTick += 1;
// Read the systick control regster. This has the side effect of clearing
// the COUNTFLAG bit, which makes the logic in sys_tick_get_microseconds
// work properly.
SysTick->CTRL;
#if 0
// Right now we have the storage and DMA controllers to process during
// this interrupt and we use custom dispatch handlers. If this needs to
// be generalised in the future then a dispatch table can be used as
// follows: ((void(*)(void))(systick_dispatch[uwTick & 0xf]))();
if (STORAGE_IDLE_TICK(uwTick)) {
NVIC->STIR = FLASH_IRQn;
}
if (DMA_IDLE_ENABLED() && DMA_IDLE_TICK(uwTick)) {
dma_idle_handler(uwTick);
}
#endif
}
// ###
int main(void) { int main(void) {
HAL_Init(); HAL_Init();
@ -101,7 +36,5 @@ int main(void) {
HAL_Delay(250); HAL_Delay(250);
} }
__fatal_error("end");
return 0; return 0;
} }

View File

@ -10,7 +10,7 @@
void screen_welcome(void) void screen_welcome(void)
{ {
display_image(0, 0, 240, 240, toi_trezor, sizeof(toi_trezor)); display_icon(0, 0, 240, 240, toi_trezor, sizeof(toi_trezor), ui_WHITE, ui_BLACK);
display_text(0, 240, "bootloader", 10, FONT_MONO, ui_WHITE, ui_BLACK); display_text(0, 240, "bootloader", 10, FONT_MONO, ui_WHITE, ui_BLACK);
} }

38
tools/check_bootloader Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env python3
import sys
import struct
# bootloader header specification: https://github.com/trezor/trezor-core/blob/master/docs/bootloader.md#bootloader-header
def check_bootloader(filename):
data = open(filename, 'rb').read()
hdr = struct.unpack('<4sIIIBBBBB64s171s', data[:256])
code = data[256:]
magic, hdrlen, expiry, codelen, vmajor, vminor, vpatch, vbuild, sigidx, sig, reserved = hdr
sigidx = tuple([ i + 1 for i in range(8) if sigidx & (1 << i) ])
print('magic :', magic)
assert magic == b'TRZB'
print('hdrlen :', hdrlen)
assert hdrlen == 256
print('expiry :', expiry)
print('codelen :', codelen)
print('vmajor :', vmajor)
print('vminor :', vminor)
print('vpatch :', vpatch)
print('vbuild :', vbuild)
print('sigidx :', sigidx)
print('sig :', sig)
assert reserved == 171 * b'\x00'
assert codelen == 64*1024
assert codelen == len(code)
def main():
if len(sys.argv) < 2:
print('Usage: check_bootloader bootloader.bin')
return 1
fn = sys.argv[1]
check_bootloader(fn)
main()

17
tools/check_firmware Executable file
View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
import sys
def process_firmware(filename):
data = open(filename, 'rb').read()
def main():
if len(sys.argv) < 2:
print('Usage: check_firmware firmware.bin')
return 1
fn = sys.argv[1]
process_firmware(fn)
main()

View File

@ -73,7 +73,7 @@ def process_image(ifn):
def main(): def main():
if len(sys.argv) < 2: if len(sys.argv) < 2:
print('Usage toi2png image.toi[fg]') print('Usage: toi2png image.toi[fg]')
return 1 return 1
ifn = sys.argv[1] ifn = sys.argv[1]