mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-14 03:30:02 +00:00
build: refactor binctl and firmware/loader image stuff
This commit is contained in:
parent
e37619899e
commit
435e96e1b3
@ -56,6 +56,7 @@ OBJ_HAL += $(addprefix $(BUILD_MP)/,\
|
|||||||
|
|
||||||
# OBJ micropython/
|
# OBJ micropython/
|
||||||
OBJ_FW += $(addprefix $(BUILD_FW)/, \
|
OBJ_FW += $(addprefix $(BUILD_FW)/, \
|
||||||
|
loader/crypto.o \
|
||||||
loader/header.o \
|
loader/header.o \
|
||||||
loader/main.o \
|
loader/main.o \
|
||||||
extmod/modtrezorui/display.o \
|
extmod/modtrezorui/display.o \
|
||||||
|
@ -48,8 +48,8 @@ Total length of loader header is always 512 bytes.
|
|||||||
| 0x0012 | 1 | vpatch | version (patch) |
|
| 0x0012 | 1 | vpatch | version (patch) |
|
||||||
| 0x0013 | 1 | vbuild | version (build) |
|
| 0x0013 | 1 | vbuild | version (build) |
|
||||||
| 0x0014 | 427 | reserved | not used yet (zeroed) |
|
| 0x0014 | 427 | reserved | not used yet (zeroed) |
|
||||||
| 0x01BF | 1 | sigidx | SatoshiLabs signature indexes (bitmap) |
|
| 0x01BF | 1 | sigmask | SatoshiLabs signature indexes (bitmap) |
|
||||||
| 0x01C0 | 64 | sig | SatoshiLabs signature |
|
| 0x01C0 | 64 | sig | SatoshiLabs aggregated signature |
|
||||||
|
|
||||||
## Firmware Format
|
## Firmware Format
|
||||||
|
|
||||||
@ -79,8 +79,8 @@ Total length of vendor header is 84 + 32 * (number of pubkeys) + (length of vend
|
|||||||
| ? | ? | vstr | vendor string |
|
| ? | ? | vstr | vendor string |
|
||||||
| ? | 2 | vimg_len | vendor image length |
|
| ? | 2 | vimg_len | vendor image length |
|
||||||
| ? | ? | vimg | vendor image (in [TOIf format](toif.md)) |
|
| ? | ? | vimg | vendor image (in [TOIf format](toif.md)) |
|
||||||
| ? | 1 | sigidx | SatoshiLabs signature indexes (bitmap) |
|
| ? | 1 | sigmask | SatoshiLabs signature indexes (bitmap) |
|
||||||
| ? | 64 | sig | SatoshiLabs signature |
|
| ? | 64 | sig | SatoshiLabs aggregated signature |
|
||||||
|
|
||||||
### Firmware Header
|
### Firmware Header
|
||||||
|
|
||||||
@ -97,8 +97,8 @@ Total length of firmware header is always 512 bytes.
|
|||||||
| 0x0012 | 1 | vpatch | version (patch) |
|
| 0x0012 | 1 | vpatch | version (patch) |
|
||||||
| 0x0013 | 1 | vbuild | version (build) |
|
| 0x0013 | 1 | vbuild | version (build) |
|
||||||
| 0x0014 | 427 | reserved | not used yet (zeroed) |
|
| 0x0014 | 427 | reserved | not used yet (zeroed) |
|
||||||
| 0x01BF | 1 | sigidx | vendor signature indexes (bitmap) |
|
| 0x01BF | 1 | sigmask | vendor signature indexes (bitmap) |
|
||||||
| 0x01C0 | 64 | sig | vendor signature |
|
| 0x01C0 | 64 | sig | vendor aggregated signature |
|
||||||
|
|
||||||
## Various ideas
|
## Various ideas
|
||||||
|
|
||||||
|
@ -3,9 +3,10 @@
|
|||||||
#include "blake2s.h"
|
#include "blake2s.h"
|
||||||
#include "ed25519-donna/ed25519.h"
|
#include "ed25519-donna/ed25519.h"
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
|
|
||||||
bool parse_header(const uint8_t *data, uint32_t *codelen, uint8_t *sigidx, uint8_t *sig)
|
bool parse_header(const uint8_t *data, uint32_t *codelen, uint8_t *sigmask, uint8_t *sig)
|
||||||
{
|
{
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
memcpy(&magic, data, 4);
|
memcpy(&magic, data, 4);
|
||||||
@ -13,7 +14,7 @@ bool parse_header(const uint8_t *data, uint32_t *codelen, uint8_t *sigidx, uint8
|
|||||||
|
|
||||||
uint32_t hdrlen;
|
uint32_t hdrlen;
|
||||||
memcpy(&hdrlen, data + 4, 4);
|
memcpy(&hdrlen, data + 4, 4);
|
||||||
if (hdrlen != 256) return false;
|
if (hdrlen != HEADER_SIZE) return false;
|
||||||
|
|
||||||
uint32_t expiry;
|
uint32_t expiry;
|
||||||
memcpy(&expiry, data + 8, 4);
|
memcpy(&expiry, data + 8, 4);
|
||||||
@ -21,7 +22,6 @@ bool parse_header(const uint8_t *data, uint32_t *codelen, uint8_t *sigidx, uint8
|
|||||||
|
|
||||||
uint32_t clen;
|
uint32_t clen;
|
||||||
memcpy(&clen, data + 12, 4);
|
memcpy(&clen, data + 12, 4);
|
||||||
// stage 2 (+header) must fit into sectors 4...11 - see docs/memory.md for more info
|
|
||||||
if (clen + hdrlen < 4 * 1024) return false;
|
if (clen + hdrlen < 4 * 1024) return false;
|
||||||
if (clen + hdrlen > 64 * 1024 + 7 * 128 * 1024) return false;
|
if (clen + hdrlen > 64 * 1024 + 7 * 128 * 1024) return false;
|
||||||
if ((clen + hdrlen) % 512 != 0) return false;
|
if ((clen + hdrlen) % 512 != 0) return false;
|
||||||
@ -33,14 +33,14 @@ bool parse_header(const uint8_t *data, uint32_t *codelen, uint8_t *sigidx, uint8
|
|||||||
uint32_t version;
|
uint32_t version;
|
||||||
memcpy(&version, data + 16, 4);
|
memcpy(&version, data + 16, 4);
|
||||||
|
|
||||||
// uint8_t reserved[171];
|
// uint8_t reserved[427];
|
||||||
|
|
||||||
if (sigidx) {
|
if (sigmask) {
|
||||||
memcpy(sigidx, data + 0x00BF, 1);
|
memcpy(sigmask, data + 0x01BF, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sig) {
|
if (sig) {
|
||||||
memcpy(sig, data + 0x00C0, 64);
|
memcpy(sig, data + 0x01C0, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -79,9 +79,9 @@ const uint8_t *get_pubkey(uint8_t index)
|
|||||||
bool check_signature(const uint8_t *start)
|
bool check_signature(const uint8_t *start)
|
||||||
{
|
{
|
||||||
uint32_t codelen;
|
uint32_t codelen;
|
||||||
uint8_t sigidx;
|
uint8_t sigmask;
|
||||||
uint8_t sig[64];
|
uint8_t sig[64];
|
||||||
if (!parse_header(start, &codelen, &sigidx, sig)) {
|
if (!parse_header(start, &codelen, &sigmask, sig)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +95,11 @@ bool check_signature(const uint8_t *start)
|
|||||||
blake2s_Update(&ctx, start + 256, codelen);
|
blake2s_Update(&ctx, start + 256, codelen);
|
||||||
blake2s_Final(&ctx, hash, BLAKE2S_DIGEST_LENGTH);
|
blake2s_Final(&ctx, hash, BLAKE2S_DIGEST_LENGTH);
|
||||||
|
|
||||||
const uint8_t *pub = get_pubkey(sigidx);
|
const uint8_t *pub = get_pubkey(sigmask);
|
||||||
|
|
||||||
|
// TODO: remove debug skip of unsigned
|
||||||
|
if (!pub) return true;
|
||||||
|
// end
|
||||||
|
|
||||||
return pub && (0 == ed25519_sign_open(hash, BLAKE2S_DIGEST_LENGTH, *(const ed25519_public_key *)pub, *(const ed25519_signature *)sig));
|
return pub && (0 == ed25519_sign_open(hash, BLAKE2S_DIGEST_LENGTH, *(const ed25519_public_key *)pub, *(const ed25519_signature *)sig));
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,27 @@ bool copy_sdcard(void)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void check_and_jump(void)
|
||||||
|
{
|
||||||
|
BOOTLOADER_PRINTLN("checking loader");
|
||||||
|
if (parse_header((const uint8_t *)LOADER_START, NULL, NULL, NULL)) {
|
||||||
|
BOOTLOADER_PRINTLN("valid loader header");
|
||||||
|
if (check_signature((const uint8_t *)LOADER_START)) {
|
||||||
|
BOOTLOADER_PRINTLN("valid loader signature");
|
||||||
|
// TODO: remove debug wait
|
||||||
|
BOOTLOADER_PRINTLN("waiting 1 second");
|
||||||
|
HAL_Delay(1000);
|
||||||
|
// end
|
||||||
|
BOOTLOADER_PRINTLN("JUMP!");
|
||||||
|
jump_to(LOADER_START + HEADER_SIZE);
|
||||||
|
} else {
|
||||||
|
BOOTLOADER_PRINTLN("invalid loader signature");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
BOOTLOADER_PRINTLN("invalid loader header");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
SCB->VTOR = BOOTLOADER_START;
|
SCB->VTOR = BOOTLOADER_START;
|
||||||
@ -127,32 +148,13 @@ int main(void)
|
|||||||
BOOTLOADER_PRINTLN("=================");
|
BOOTLOADER_PRINTLN("=================");
|
||||||
BOOTLOADER_PRINTLN("starting bootloader");
|
BOOTLOADER_PRINTLN("starting bootloader");
|
||||||
|
|
||||||
// TODO: remove debug
|
|
||||||
BOOTLOADER_PRINTLN("waiting 1 second");
|
|
||||||
HAL_Delay(1000);
|
|
||||||
BOOTLOADER_PRINTLN("jumping to loader");
|
|
||||||
jump_to(LOADER_START + HEADER_SIZE);
|
|
||||||
// end
|
|
||||||
|
|
||||||
if (check_sdcard()) {
|
if (check_sdcard()) {
|
||||||
if (!copy_sdcard()) {
|
if (!copy_sdcard()) {
|
||||||
__fatal_error("halt");
|
__fatal_error("halt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOTLOADER_PRINTLN("checking loader");
|
check_and_jump();
|
||||||
if (parse_header((const uint8_t *)LOADER_START, NULL, NULL, NULL)) {
|
|
||||||
BOOTLOADER_PRINTLN("valid loader header");
|
|
||||||
if (check_signature((const uint8_t *)LOADER_START)) {
|
|
||||||
BOOTLOADER_PRINTLN("valid loader signature");
|
|
||||||
BOOTLOADER_PRINTLN("JUMP!");
|
|
||||||
jump_to(LOADER_START + HEADER_SIZE);
|
|
||||||
} else {
|
|
||||||
BOOTLOADER_PRINTLN("invalid loader signature");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
BOOTLOADER_PRINTLN("invalid loader header");
|
|
||||||
}
|
|
||||||
|
|
||||||
__fatal_error("halt");
|
__fatal_error("halt");
|
||||||
|
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
.section .header,"a",%progbits
|
.section .header,"a",%progbits
|
||||||
|
|
||||||
.type g_header, %object
|
.type g_header, %object
|
||||||
.size g_header, .-g_header
|
.size g_header, .-g_header
|
||||||
|
|
||||||
g_header:
|
g_header:
|
||||||
.byte 'T','R','Z','F'
|
.byte 'T','R','Z','F' // magic
|
||||||
.word g_header_end - g_header
|
.word g_header_end - g_header // hdrlen
|
||||||
.word 0 /* valid until */
|
.word 0 // expiry
|
||||||
.word _codelen
|
.word _codelen // codelen
|
||||||
.byte VERSION_MAJOR
|
.byte VERSION_MAJOR // vmajor
|
||||||
.byte VERSION_MINOR
|
.byte VERSION_MINOR // vminor
|
||||||
.byte VERSION_PATCH
|
.byte VERSION_PATCH // vpatch
|
||||||
.byte VERSION_BUILD
|
.byte VERSION_BUILD // vbuild
|
||||||
. = . + 427 /* reserved */
|
. = . + 427 // reserved
|
||||||
.byte 0 /* sigindex */
|
.byte 0 // sigmask
|
||||||
. = . + 64 /* signatures */
|
. = . + 64 // sig
|
||||||
g_header_end:
|
g_header_end:
|
||||||
|
@ -34,7 +34,7 @@ SECTIONS
|
|||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
*(.text*) /* .text* sections (code) */
|
*(.text*) /* .text* sections (code) */
|
||||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||||
. = ALIGN(4);
|
. = ALIGN(512);
|
||||||
_etext = .; /* define a global symbol at end of code */
|
_etext = .; /* define a global symbol at end of code */
|
||||||
} >FLASH
|
} >FLASH
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ SECTIONS
|
|||||||
_sdata = .; /* create a global symbol at data start; used by startup code in order to initialise the .data section in RAM */
|
_sdata = .; /* create a global symbol at data start; used by startup code in order to initialise the .data section in RAM */
|
||||||
*(.data*) /* .data* sections */
|
*(.data*) /* .data* sections */
|
||||||
|
|
||||||
. = ALIGN(4);
|
. = ALIGN(512);
|
||||||
_edata = .; /* define a global symbol at data end; used by startup code in order to initialise the .data section in RAM */
|
_edata = .; /* define a global symbol at data end; used by startup code in order to initialise the .data section in RAM */
|
||||||
} >RAM AT> FLASH
|
} >RAM AT> FLASH
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ SECTIONS
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* RAM extents for the garbage collector */
|
/* RAM extents for the garbage collector */
|
||||||
_codelen = SIZEOF(.flash) + SIZEOF(.data);
|
_codelen = SIZEOF(.flash) + SIZEOF(.data) - 512;
|
||||||
_ram_start = ORIGIN(RAM);
|
_ram_start = ORIGIN(RAM);
|
||||||
_ram_end = ORIGIN(RAM) + LENGTH(RAM);
|
_ram_end = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
_heap_start = _ebss; /* heap starts just after statically allocated memory */
|
_heap_start = _ebss; /* heap starts just after statically allocated memory */
|
||||||
|
105
micropython/loader/crypto.c
Normal file
105
micropython/loader/crypto.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "blake2s.h"
|
||||||
|
#include "ed25519-donna/ed25519.h"
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "crypto.h"
|
||||||
|
|
||||||
|
bool parse_header(const uint8_t *data, uint32_t *codelen, uint8_t *sigmask, uint8_t *sig)
|
||||||
|
{
|
||||||
|
uint32_t magic;
|
||||||
|
memcpy(&magic, data, 4);
|
||||||
|
if (magic != 0x465A5254) return false; // TRZF
|
||||||
|
|
||||||
|
uint32_t hdrlen;
|
||||||
|
memcpy(&hdrlen, data + 4, 4);
|
||||||
|
if (hdrlen != HEADER_SIZE) return false;
|
||||||
|
|
||||||
|
uint32_t expiry;
|
||||||
|
memcpy(&expiry, data + 8, 4);
|
||||||
|
if (expiry != 0) return false;
|
||||||
|
|
||||||
|
uint32_t clen;
|
||||||
|
memcpy(&clen, data + 12, 4);
|
||||||
|
if (clen + hdrlen < 4 * 1024) return false;
|
||||||
|
if (clen + hdrlen > 7 * 128 * 1024) return false;
|
||||||
|
if ((clen + hdrlen) % 512 != 0) return false;
|
||||||
|
|
||||||
|
if (codelen) {
|
||||||
|
*codelen = clen;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t version;
|
||||||
|
memcpy(&version, data + 16, 4);
|
||||||
|
|
||||||
|
// uint8_t reserved[427];
|
||||||
|
|
||||||
|
if (sigmask) {
|
||||||
|
memcpy(sigmask, data + 0x01BF, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sig) {
|
||||||
|
memcpy(sig, data + 0x01C0, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define KEYMASK(A, B, C) ((1 << (A - 1)) | (1 << (B - 1)) | (1 << (C - 1)))
|
||||||
|
|
||||||
|
const uint8_t *get_pubkey(uint8_t index)
|
||||||
|
{
|
||||||
|
switch (index) {
|
||||||
|
case KEYMASK(1, 2, 3):
|
||||||
|
return (const uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
case KEYMASK(1, 2, 4):
|
||||||
|
return (const uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
case KEYMASK(1, 2, 5):
|
||||||
|
return (const uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
case KEYMASK(1, 3, 4):
|
||||||
|
return (const uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
case KEYMASK(1, 3, 5):
|
||||||
|
return (const uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
case KEYMASK(1, 4, 5):
|
||||||
|
return (const uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
case KEYMASK(2, 3, 4):
|
||||||
|
return (const uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
case KEYMASK(2, 3, 5):
|
||||||
|
return (const uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
case KEYMASK(2, 4, 5):
|
||||||
|
return (const uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
case KEYMASK(3, 4, 5):
|
||||||
|
return (const uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_signature(const uint8_t *start)
|
||||||
|
{
|
||||||
|
uint32_t codelen;
|
||||||
|
uint8_t sigmask;
|
||||||
|
uint8_t sig[64];
|
||||||
|
if (!parse_header(start, &codelen, &sigmask, sig)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t hash[BLAKE2S_DIGEST_LENGTH];
|
||||||
|
BLAKE2S_CTX ctx;
|
||||||
|
blake2s_Init(&ctx, BLAKE2S_DIGEST_LENGTH);
|
||||||
|
blake2s_Update(&ctx, start, 256 - 65);
|
||||||
|
for (int i = 0; i < 65; i++) {
|
||||||
|
blake2s_Update(&ctx, (const uint8_t *)"\x00", 1);
|
||||||
|
}
|
||||||
|
blake2s_Update(&ctx, start + 256, codelen);
|
||||||
|
blake2s_Final(&ctx, hash, BLAKE2S_DIGEST_LENGTH);
|
||||||
|
|
||||||
|
const uint8_t *pub = get_pubkey(sigmask);
|
||||||
|
|
||||||
|
// TODO: remove debug skip of unsigned
|
||||||
|
if (!pub) return true;
|
||||||
|
// end
|
||||||
|
|
||||||
|
return pub && (0 == ed25519_sign_open(hash, BLAKE2S_DIGEST_LENGTH, *(const ed25519_public_key *)pub, *(const ed25519_signature *)sig));
|
||||||
|
}
|
11
micropython/loader/crypto.h
Normal file
11
micropython/loader/crypto.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef __LOADER_CRYPTO_H__
|
||||||
|
#define __LOADER_CRYPTO_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
bool parse_header(const uint8_t *data, uint32_t *codelen, uint8_t *sigidx, uint8_t *sig);
|
||||||
|
|
||||||
|
bool check_signature(const uint8_t *start);
|
||||||
|
|
||||||
|
#endif
|
@ -1,20 +1,20 @@
|
|||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
.section .header,"a",%progbits
|
.section .header,"a",%progbits
|
||||||
|
|
||||||
.type g_header, %object
|
.type g_header, %object
|
||||||
.size g_header, .-g_header
|
.size g_header, .-g_header
|
||||||
|
|
||||||
g_header:
|
g_header:
|
||||||
.byte 'T','R','Z','L'
|
.byte 'T','R','Z','L' // magic
|
||||||
.word g_header_end - g_header
|
.word g_header_end - g_header // hdrlen
|
||||||
.word 0 /* valid until */
|
.word 0 // expiry
|
||||||
.word _codelen
|
.word _codelen // codelen
|
||||||
.byte VERSION_MAJOR
|
.byte VERSION_MAJOR // vmajor
|
||||||
.byte VERSION_MINOR
|
.byte VERSION_MINOR // vminor
|
||||||
.byte VERSION_PATCH
|
.byte VERSION_PATCH // vpatch
|
||||||
.byte VERSION_BUILD
|
.byte VERSION_BUILD // vbuild
|
||||||
. = . + 427 /* reserved */
|
. = . + 427 // reserved
|
||||||
.byte 0 /* sigindex */
|
.byte 0 // sigmask
|
||||||
. = . + 64 /* signatures */
|
. = . + 64 // sig
|
||||||
g_header_end:
|
g_header_end:
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
|
#include "crypto.h"
|
||||||
|
|
||||||
#define LOADER_FGCOLOR 0xFFFF
|
#define LOADER_FGCOLOR 0xFFFF
|
||||||
#define LOADER_BGCOLOR 0x0000
|
#define LOADER_BGCOLOR 0x0000
|
||||||
@ -13,6 +14,27 @@ void pendsv_isr_handler(void) {
|
|||||||
__fatal_error("pendsv");
|
__fatal_error("pendsv");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void check_and_jump(void)
|
||||||
|
{
|
||||||
|
LOADER_PRINTLN("checking firmware");
|
||||||
|
if (parse_header((const uint8_t *)FIRMWARE_START, NULL, NULL, NULL)) {
|
||||||
|
LOADER_PRINTLN("valid firmware header");
|
||||||
|
if (check_signature((const uint8_t *)FIRMWARE_START)) {
|
||||||
|
LOADER_PRINTLN("valid firmware signature");
|
||||||
|
LOADER_PRINTLN("JUMP!");
|
||||||
|
// TODO: remove debug wait
|
||||||
|
LOADER_PRINTLN("waiting 1 second");
|
||||||
|
HAL_Delay(1000);
|
||||||
|
// end
|
||||||
|
jump_to(FIRMWARE_START + HEADER_SIZE);
|
||||||
|
} else {
|
||||||
|
LOADER_PRINTLN("invalid firmware signature");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOADER_PRINTLN("invalid firmware header");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
SCB->VTOR = LOADER_START + HEADER_SIZE;
|
SCB->VTOR = LOADER_START + HEADER_SIZE;
|
||||||
@ -22,12 +44,13 @@ int main(void)
|
|||||||
display_clear();
|
display_clear();
|
||||||
display_backlight(255);
|
display_backlight(255);
|
||||||
|
|
||||||
LOADER_PRINTLN("reached loader");
|
LOADER_PRINTLN("TREZOR Loader");
|
||||||
LOADER_PRINTLN("waiting 1 second");
|
LOADER_PRINTLN("=============");
|
||||||
HAL_Delay(1000);
|
LOADER_PRINTLN("starting loader");
|
||||||
LOADER_PRINTLN("jumping to firmware");
|
|
||||||
|
|
||||||
jump_to(FIRMWARE_START + HEADER_SIZE);
|
check_and_jump();
|
||||||
|
|
||||||
|
__fatal_error("halt");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -29,12 +29,12 @@ SECTIONS
|
|||||||
.flash :
|
.flash :
|
||||||
{
|
{
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
KEEP(*(.header))
|
KEEP(*(.header)) /* Firmware Header */
|
||||||
KEEP(*(.isr_vector)) /* Startup code */
|
KEEP(*(.isr_vector)) /* Startup code */
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
*(.text*) /* .text* sections (code) */
|
*(.text*) /* .text* sections (code) */
|
||||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||||
. = ALIGN(4);
|
. = ALIGN(512);
|
||||||
_etext = .; /* define a global symbol at end of code */
|
_etext = .; /* define a global symbol at end of code */
|
||||||
} >FLASH
|
} >FLASH
|
||||||
|
|
||||||
@ -51,9 +51,8 @@ SECTIONS
|
|||||||
_sdata = .; /* create a global symbol at data start; used by startup code in order to initialise the .data section in RAM */
|
_sdata = .; /* create a global symbol at data start; used by startup code in order to initialise the .data section in RAM */
|
||||||
*(.data*) /* .data* sections */
|
*(.data*) /* .data* sections */
|
||||||
|
|
||||||
. = ALIGN(4);
|
. = ALIGN(512);
|
||||||
_edata = .; /* define a global symbol at data end; used by startup code in order to initialise the .data section in RAM */
|
_edata = .; /* define a global symbol at data end; used by startup code in order to initialise the .data section in RAM */
|
||||||
_datalen = . - _sdata;
|
|
||||||
} >RAM AT> FLASH
|
} >RAM AT> FLASH
|
||||||
|
|
||||||
/* Uninitialized data section */
|
/* Uninitialized data section */
|
||||||
@ -88,7 +87,7 @@ SECTIONS
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* RAM extents for the garbage collector */
|
/* RAM extents for the garbage collector */
|
||||||
_codelen = SIZEOF(.flash) + SIZEOF(.data);
|
_codelen = SIZEOF(.flash) + SIZEOF(.data) - 512;
|
||||||
_ram_start = ORIGIN(RAM);
|
_ram_start = ORIGIN(RAM);
|
||||||
_ram_end = ORIGIN(RAM) + LENGTH(RAM);
|
_ram_end = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
_heap_start = _ebss; /* heap starts just after statically allocated memory */
|
_heap_start = _ebss; /* heap starts just after statically allocated memory */
|
||||||
|
122
tools/binctl
122
tools/binctl
@ -6,22 +6,22 @@ import ed25519
|
|||||||
import pyblake2
|
import pyblake2
|
||||||
|
|
||||||
|
|
||||||
# loader/firmware headers specification: https://github.com/trezor/trezor-core/blob/master/docs/bootloader.md
|
|
||||||
|
|
||||||
|
|
||||||
def get_sig(data):
|
def get_sig(data):
|
||||||
print('Enter privkey: ', end='')
|
print('Enter privkey: ', end='')
|
||||||
seckey = binascii.unhexlify(input())
|
seckey = binascii.unhexlify(input())
|
||||||
signkey = ed25519.SigningKey(seckey)
|
signkey = ed25519.SigningKey(seckey)
|
||||||
digest = pyblake2.blake2s(data).digest()
|
digest = pyblake2.blake2s(data).digest()
|
||||||
sigidx = 0x01 # (1 _ _ _ _)
|
sigmask = 0x01 # (1 _ _ _ _)
|
||||||
sig = signkey.sign(digest)
|
sig = signkey.sign(digest)
|
||||||
return sigidx, sig
|
return sigmask, sig
|
||||||
|
|
||||||
|
|
||||||
class LoaderImage:
|
# loader/firmware headers specification: https://github.com/trezor/trezor-core/blob/master/docs/bootloader.md
|
||||||
|
|
||||||
def __init__(self, data):
|
|
||||||
|
class BinImage:
|
||||||
|
|
||||||
|
def __init__(self, data, magic, max_size):
|
||||||
header = struct.unpack('<4sIIIBBBB427sB64s', data[:512])
|
header = struct.unpack('<4sIIIBBBB427sB64s', data[:512])
|
||||||
self.magic, \
|
self.magic, \
|
||||||
self.hdrlen, \
|
self.hdrlen, \
|
||||||
@ -32,25 +32,31 @@ class LoaderImage:
|
|||||||
self.vpatch, \
|
self.vpatch, \
|
||||||
self.vbuild, \
|
self.vbuild, \
|
||||||
self.reserved, \
|
self.reserved, \
|
||||||
self.sigidx, \
|
self.sigmask, \
|
||||||
self.sig = header
|
self.sig = header
|
||||||
assert self.magic == b'TRZL'
|
assert self.magic == magic
|
||||||
assert self.hdrlen == 512
|
assert self.hdrlen == 512
|
||||||
assert self.codelen + self.hdrlen >= 4 * 1024
|
total_len = self.hdrlen + self.codelen
|
||||||
assert self.codelen + self.hdrlen <= 64 * 1024 + 7 * 128 * 1024
|
assert total_len % 512 == 0
|
||||||
assert (self.codelen + self.hdrlen) % 512 == 0
|
assert total_len >= 4 * 1024
|
||||||
|
assert total_len <= max_size
|
||||||
assert self.reserved == 427 * b'\x00'
|
assert self.reserved == 427 * b'\x00'
|
||||||
self.code = data[self.hdrlen:]
|
self.code = data[self.hdrlen:]
|
||||||
assert len(self.code) == self.codelen
|
assert len(self.code) == self.codelen
|
||||||
|
|
||||||
def print(self):
|
def print(self):
|
||||||
print('TREZOR Loader Image')
|
if self.magic == b'TRZF':
|
||||||
|
print('TREZOR Firmware Image')
|
||||||
|
elif self.magic == b'TRZL':
|
||||||
|
print('TREZOR Loader Image')
|
||||||
|
else:
|
||||||
|
print('TREZOR Unknown Image')
|
||||||
print(' * magic :', self.magic.decode('ascii'))
|
print(' * magic :', self.magic.decode('ascii'))
|
||||||
print(' * hdrlen :', self.hdrlen)
|
print(' * hdrlen :', self.hdrlen)
|
||||||
print(' * expiry :', self.expiry)
|
print(' * expiry :', self.expiry)
|
||||||
print(' * codelen :', self.codelen)
|
print(' * codelen :', self.codelen)
|
||||||
print(' * version : %d.%d.%d.%d' % (self.vmajor, self.vminor, self.vpatch, self.vbuild))
|
print(' * version : %d.%d.%d.%d' % (self.vmajor, self.vminor, self.vpatch, self.vbuild))
|
||||||
print(' * sigidx :', self.sigidx)
|
print(' * sigmask :', self.sigmask)
|
||||||
print(' * sig :', binascii.hexlify(self.sig).decode('ascii'))
|
print(' * sig :', binascii.hexlify(self.sig).decode('ascii'))
|
||||||
|
|
||||||
def serialize_header(self, sig=True):
|
def serialize_header(self, sig=True):
|
||||||
@ -59,7 +65,7 @@ class LoaderImage:
|
|||||||
self.vmajor, self.vminor, self.vpatch, self.vbuild, \
|
self.vmajor, self.vminor, self.vpatch, self.vbuild, \
|
||||||
self.reserved)
|
self.reserved)
|
||||||
if sig:
|
if sig:
|
||||||
header += struct.pack('<B64s', self.sigidx, self.sig)
|
header += struct.pack('<B64s', self.sigmask, self.sig)
|
||||||
else:
|
else:
|
||||||
header += 65 * b'\x00'
|
header += 65 * b'\x00'
|
||||||
assert len(header) == self.hdrlen
|
assert len(header) == self.hdrlen
|
||||||
@ -69,7 +75,7 @@ class LoaderImage:
|
|||||||
header = self.serialize_header(sig=False)
|
header = self.serialize_header(sig=False)
|
||||||
data = header + self.code
|
data = header + self.code
|
||||||
assert len(data) == self.hdrlen + self.codelen
|
assert len(data) == self.hdrlen + self.codelen
|
||||||
self.sigidx, self.sig = get_sig(data)
|
self.sigmask, self.sig = get_sig(data)
|
||||||
|
|
||||||
def write(self, filename):
|
def write(self, filename):
|
||||||
with open(filename, 'wb') as f:
|
with open(filename, 'wb') as f:
|
||||||
@ -77,6 +83,19 @@ class LoaderImage:
|
|||||||
f.write(self.code)
|
f.write(self.code)
|
||||||
|
|
||||||
|
|
||||||
|
class FirmwareImage(BinImage):
|
||||||
|
|
||||||
|
def __init__(self, data):
|
||||||
|
super().__init__(data, magic=b'TRZF', max_size=7*128*1024)
|
||||||
|
|
||||||
|
|
||||||
|
class LoaderImage(BinImage):
|
||||||
|
|
||||||
|
def __init__(self, data):
|
||||||
|
super().__init__(data, magic=b'TRZL', max_size=64*1024 + 7*128*1024)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
class VendorHeader:
|
class VendorHeader:
|
||||||
|
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
@ -104,7 +123,7 @@ class VendorHeader:
|
|||||||
p += 2
|
p += 2
|
||||||
self.vimg = data[p:p + self.vimg_len]
|
self.vimg = data[p:p + self.vimg_len]
|
||||||
p += self.vimg_len
|
p += self.vimg_len
|
||||||
self.sigidx = data[p]
|
self.sigmask = data[p]
|
||||||
p += 1
|
p += 1
|
||||||
self.sig = data[p:p+64]
|
self.sig = data[p:p+64]
|
||||||
assert len(data) == 4 + 4 + 4 + 1 + 1 + 1 + 1 + \
|
assert len(data) == 4 + 4 + 4 + 1 + 1 + 1 + 1 + \
|
||||||
@ -124,8 +143,8 @@ class VendorHeader:
|
|||||||
print(' * vpub #%d :' % (i + 1), binascii.hexlify(self.vpub[i]).decode('ascii'))
|
print(' * vpub #%d :' % (i + 1), binascii.hexlify(self.vpub[i]).decode('ascii'))
|
||||||
print(' * vstr :', self.vstr.decode('ascii'))
|
print(' * vstr :', self.vstr.decode('ascii'))
|
||||||
print(' * vimg : (%d bytes)', len(self.vimg))
|
print(' * vimg : (%d bytes)', len(self.vimg))
|
||||||
print(' * sigidx :', self.sigidx)
|
print(' * sigmask :', self.sigmask)
|
||||||
print(' * sig :', binascii.hexlify(self.sig).decode('ascii'))
|
print(' * sig :', binascii.hexlify(self.sig).decode('ascii'))
|
||||||
|
|
||||||
def serialize_header(self, sig=True):
|
def serialize_header(self, sig=True):
|
||||||
header = struct.pack('<4sIIBBBB', \
|
header = struct.pack('<4sIIBBBB', \
|
||||||
@ -137,7 +156,7 @@ class VendorHeader:
|
|||||||
header += struct.pack('<B', self.vstr_len) + self.vstr
|
header += struct.pack('<B', self.vstr_len) + self.vstr
|
||||||
header += struct.pack('<H', self.vimg_len) + self.vimg
|
header += struct.pack('<H', self.vimg_len) + self.vimg
|
||||||
if sig:
|
if sig:
|
||||||
header += struct.pack('<B64s', self.sigidx, self.sig)
|
header += struct.pack('<B64s', self.sigmask, self.sig)
|
||||||
else:
|
else:
|
||||||
header += 65 * b'\x00'
|
header += 65 * b'\x00'
|
||||||
assert len(header) == self.hdrlen
|
assert len(header) == self.hdrlen
|
||||||
@ -145,67 +164,12 @@ class VendorHeader:
|
|||||||
|
|
||||||
def sign(self):
|
def sign(self):
|
||||||
header = self.serialize_header(sig=False)
|
header = self.serialize_header(sig=False)
|
||||||
self.sigidx, self.sig = get_sig(header)
|
self.sigmask, self.sig = get_sig(header)
|
||||||
|
|
||||||
def write(self, filename):
|
def write(self, filename):
|
||||||
with open(filename, 'wb') as f:
|
with open(filename, 'wb') as f:
|
||||||
f.write(self.serialize_header())
|
f.write(self.serialize_header())
|
||||||
|
"""
|
||||||
|
|
||||||
class FirmwareImage:
|
|
||||||
|
|
||||||
def __init__(self, data):
|
|
||||||
header = struct.unpack('<4sIIIBBBB427sB64s', data[:512])
|
|
||||||
self.magic, \
|
|
||||||
self.hdrlen, \
|
|
||||||
self.expiry, \
|
|
||||||
self.codelen, \
|
|
||||||
self.vmajor, \
|
|
||||||
self.vminor, \
|
|
||||||
self.vpatch, \
|
|
||||||
self.vbuild, \
|
|
||||||
self.reserved, \
|
|
||||||
self.sigidx, \
|
|
||||||
self.sig = header
|
|
||||||
assert self.magic == b'TRZF'
|
|
||||||
assert self.hdrlen == 512
|
|
||||||
assert self.codelen % 4 == 0
|
|
||||||
assert self.reserved == 427 * b'\x00'
|
|
||||||
self.code = data[self.hdrlen:]
|
|
||||||
assert len(self.code) == self.codelen
|
|
||||||
|
|
||||||
def print(self):
|
|
||||||
print('TREZOR Firmware Image')
|
|
||||||
print(' * magic :', self.magic.decode('ascii'))
|
|
||||||
print(' * hdrlen :', self.hdrlen)
|
|
||||||
print(' * expiry :', self.expiry)
|
|
||||||
print(' * codelen :', self.codelen)
|
|
||||||
print(' * version : %d.%d.%d.%d' % (self.vmajor, self.vminor, self.vpatch, self.vbuild))
|
|
||||||
print(' * sigidx :', self.sigidx)
|
|
||||||
print(' * sig :', binascii.hexlify(self.sig).decode('ascii'))
|
|
||||||
|
|
||||||
def serialize_header(self, sig=True):
|
|
||||||
header = struct.pack('<4sIIIBBBB427s', \
|
|
||||||
self.magic, self.hdrlen, self.expiry, self.codelen, \
|
|
||||||
self.vmajor, self.vminor, self.vpatch, self.vbuild, \
|
|
||||||
self.reserved)
|
|
||||||
if sig:
|
|
||||||
header += struct.pack('<B64s', self.sigidx, self.sig)
|
|
||||||
else:
|
|
||||||
header += 65 * b'\x00'
|
|
||||||
assert len(header) == self.hdrlen
|
|
||||||
return header
|
|
||||||
|
|
||||||
def sign(self):
|
|
||||||
header = self.serialize_header(sig=False)
|
|
||||||
data = header + self.code
|
|
||||||
assert len(data) == self.hdrlen + self.codelen
|
|
||||||
self.sigidx, self.sig = get_sig(data)
|
|
||||||
|
|
||||||
def write(self, filename):
|
|
||||||
with open(filename, 'wb') as f:
|
|
||||||
f.write(self.serialize_header())
|
|
||||||
f.write(self.code)
|
|
||||||
|
|
||||||
|
|
||||||
def binopen(filename):
|
def binopen(filename):
|
||||||
@ -216,10 +180,10 @@ def binopen(filename):
|
|||||||
magic = data[:4]
|
magic = data[:4]
|
||||||
if magic == b'TRZL':
|
if magic == b'TRZL':
|
||||||
return LoaderImage(data)
|
return LoaderImage(data)
|
||||||
if magic == b'TRZV':
|
|
||||||
return VendorHeader(data)
|
|
||||||
if magic == b'TRZF':
|
if magic == b'TRZF':
|
||||||
return FirmwareImage(data)
|
return FirmwareImage(data)
|
||||||
|
# if magic == b'TRZV':
|
||||||
|
# return VendorHeader(data)
|
||||||
raise Exception('Unknown file format')
|
raise Exception('Unknown file format')
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user