diff --git a/Makefile b/Makefile index 09a7fdd6e..c3f12af06 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ vendor: ## update git submodules git submodule update --init 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 diff --git a/docs/bootloader.md b/docs/bootloader.md index 3c1657760..4bb3965f7 100644 --- a/docs/bootloader.md +++ b/docs/bootloader.md @@ -1,15 +1,26 @@ #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. -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 it is non-upgradable. Only second stage bootloader update is allowed. -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 -* 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. ##Bootloader Format @@ -19,6 +30,8 @@ TREZOR Core (second stage) bootloader consists of 2 parts: 1. bootloader header 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 Total length of bootloader header is 256 bytes. @@ -26,16 +39,16 @@ Total length of bootloader header is 256 bytes. | offset | length | name | description | |-------:|-------:|------|-------------| | 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) | -| 0x000C | 4 | codelen | length of the bootloader code | +| 0x000C | 4 | codelen | length of the bootloader 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 | slsigidx | SatoshiLabs signature indexes (bitmap) | -| 0x0015 | 64 | slsig | SatoshiLabs signature | -| 0x0079 | 135 | reserved | not used yet | +| 0x0014 | 1 | sigidx | SatoshiLabs signature indexes (bitmap) | +| 0x0015 | 64 | sig | SatoshiLabs signature | +| 0x0055 | 171 | reserved | not used yet (zeroed) | ##Firmware Format @@ -45,6 +58,8 @@ TREZOR Core firmware consists of 3 parts: 2. firmware header 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 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 | |-------:|-------:|------|-------------| | 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) | | 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 | -| 0x000E | 2 | reserved | not used yet | +| 0x000E | 2 | reserved | not used yet (zeroed) | | 0x0010 | 32 | vpub1 | vendor pubkey 1 | | ... | ... | ... | ... | | ? | 32 | vpubn | vendor pubkey n | | ? | 1 | vstr_len | vendor string length | | ? | ? | vstr | vendor string | | ? | ? | vimg | vendor image (in [TOIf format](toif.md)) | -| ? | 1 | slsigidx | SatoshiLabs signature indexes (bitmap) | -| ? | 64 | slsig | SatoshiLabs signature | +| ? | 1 | sigidx | SatoshiLabs signature indexes (bitmap) | +| ? | 64 | sig | SatoshiLabs signature | ###Firmware Header @@ -73,16 +88,16 @@ Total length of firmware header is 256 bytes. | offset | length | name | description | |-------:|-------:|------|-------------| | 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) | -| 0x000C | 4 | codelen | length of the firmware code | +| 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 | vndsigidx | vendor signature indexes (bitmap) | -| 0x0015 | 64 | vndsig | vendor signature | -| 0x0079 | 135 | reserved | not used yet | +| 0x0014 | 1 | sigidx | vendor signature indexes (bitmap) | +| 0x0015 | 64 | sig | vendor signature | +| 0x0055 | 171 | reserved | not used yet (zeroed) | ##Various ideas diff --git a/docs/memory.md b/docs/memory.md index 5df2dd437..c8673e7c8 100644 --- a/docs/memory.md +++ b/docs/memory.md @@ -4,18 +4,18 @@ | sector | range | size | function |-----------|-------------------------|--------:|---------------------- -| Sector 0 | 0x08000000 - 0x08003FFF | 16 KiB | bootloader 1st stage -| Sector 1 | 0x08004000 - 0x08007FFF | 16 KiB | storage area +| Sector 0 | 0x08000000 - 0x08003FFF | 16 KiB | bootloader 1st stage (write-protected) +| Sector 1 | 0x08004000 - 0x08007FFF | 16 KiB | bootloader 1st stage (write-protected) | Sector 2 | 0x08008000 - 0x0800BFFF | 16 KiB | storage area | Sector 3 | 0x0800C000 - 0x0800FFFF | 16 KiB | storage area | Sector 4 | 0x08010000 - 0x0801FFFF | 64 KiB | bootloader 2nd stage -| Sector 5 | 0x08020000 - 0x0803FFFF | 128 KiB | application code -| Sector 6 | 0x08040000 - 0x0805FFFF | 128 KiB | application code -| Sector 7 | 0x08060000 - 0x0807FFFF | 128 KiB | application code -| Sector 8 | 0x08080000 - 0x0809FFFF | 128 KiB | application code -| Sector 9 | 0x080A0000 - 0x080BFFFF | 128 KiB | application code -| Sector 10 | 0x080C0000 - 0x080DFFFF | 128 KiB | application code -| Sector 11 | 0x080E0000 - 0x080FFFFF | 128 KiB | application code +| Sector 5 | 0x08020000 - 0x0803FFFF | 128 KiB | firmware +| Sector 6 | 0x08040000 - 0x0805FFFF | 128 KiB | firmware +| Sector 7 | 0x08060000 - 0x0807FFFF | 128 KiB | firmware +| Sector 8 | 0x08080000 - 0x0809FFFF | 128 KiB | firmware +| Sector 9 | 0x080A0000 - 0x080BFFFF | 128 KiB | firmware +| Sector 10 | 0x080C0000 - 0x080DFFFF | 128 KiB | firmware +| Sector 11 | 0x080E0000 - 0x080FFFFF | 128 KiB | firmware ##RAM diff --git a/micropython/stmhal/Makefile.bootloader b/micropython/stmhal/Makefile.bootloader index 708818d30..7d1a7689c 100644 --- a/micropython/stmhal/Makefile.bootloader +++ b/micropython/stmhal/Makefile.bootloader @@ -80,6 +80,7 @@ SRC_LIB = $(addprefix lib/,\ ) SRC_C = \ + bootloader/basic.c \ bootloader/bootloader.c \ bootloader/crypto.c \ bootloader/ui.c \ diff --git a/micropython/stmhal/bootloader/basic.c b/micropython/stmhal/bootloader/basic.c new file mode 100644 index 000000000..81625ffad --- /dev/null +++ b/micropython/stmhal/bootloader/basic.c @@ -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; +} + +// ### diff --git a/micropython/stmhal/bootloader/bootloader.c b/micropython/stmhal/bootloader/bootloader.c index a31f271ea..06eb23000 100644 --- a/micropython/stmhal/bootloader/bootloader.c +++ b/micropython/stmhal/bootloader/bootloader.c @@ -2,75 +2,10 @@ #include "crypto.h" #include "ui.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); -// ### 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) { HAL_Init(); @@ -101,7 +36,5 @@ int main(void) { HAL_Delay(250); } - __fatal_error("end"); - return 0; } diff --git a/micropython/stmhal/bootloader/ui.c b/micropython/stmhal/bootloader/ui.c index 2097152de..6aaa12879 100644 --- a/micropython/stmhal/bootloader/ui.c +++ b/micropython/stmhal/bootloader/ui.c @@ -10,7 +10,7 @@ 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); } diff --git a/tools/build_pb.sh b/tools/build_protobuf similarity index 100% rename from tools/build_pb.sh rename to tools/build_protobuf diff --git a/tools/check_bootloader b/tools/check_bootloader new file mode 100755 index 000000000..619a35735 --- /dev/null +++ b/tools/check_bootloader @@ -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() diff --git a/tools/check_firmware b/tools/check_firmware new file mode 100755 index 000000000..7412a1238 --- /dev/null +++ b/tools/check_firmware @@ -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() diff --git a/tools/coins-gen.py b/tools/coins_gen similarity index 100% rename from tools/coins-gen.py rename to tools/coins_gen diff --git a/tools/res_collect.py b/tools/res_collect similarity index 100% rename from tools/res_collect.py rename to tools/res_collect diff --git a/tools/toi2png b/tools/toi2png index c36fe5c7e..98078d454 100755 --- a/tools/toi2png +++ b/tools/toi2png @@ -73,7 +73,7 @@ def process_image(ifn): def main(): if len(sys.argv) < 2: - print('Usage toi2png image.toi[fg]') + print('Usage: toi2png image.toi[fg]') return 1 ifn = sys.argv[1]