mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-29 10:58:21 +00:00
Implemented vendor header.
Header is generated with ./tools/build_vendorheader 'key1,key2,key3' 2 1.1 SatoshiLabs assets/satoshilabs.png micropython/firmware/vendorheader.bin where - keyN is a 64 character hex string encoding the public key - 2 encodes 2/3 key scheme - 1.1 is the version number (major, minor) - SatoshiLabs is the vendor name - satoshilabs.png is the vendor image Updated the firmware compilation that it adds vendor header and updated loader that it handles vendor header to be present.
This commit is contained in:
parent
300daa4513
commit
e0fd890661
@ -311,6 +311,7 @@ OBJ_HAL += $(addprefix $(BUILD_MP)/,\
|
|||||||
|
|
||||||
# OBJ micropython/
|
# OBJ micropython/
|
||||||
OBJ_FW += $(addprefix $(BUILD_FW)/, \
|
OBJ_FW += $(addprefix $(BUILD_FW)/, \
|
||||||
|
firmware/vendorheader.o \
|
||||||
firmware/header.o \
|
firmware/header.o \
|
||||||
firmware/main.o \
|
firmware/main.o \
|
||||||
firmware/mphalport.o \
|
firmware/mphalport.o \
|
||||||
@ -399,6 +400,11 @@ QSTR_GEN_EXTRA_CFLAGS += -DNO_QSTR -DN_X64 -DN_X86 -DN_THUMB -DN_ARM -DN_XTENSA
|
|||||||
|
|
||||||
all: $(BUILD)/$(TARGET).bin
|
all: $(BUILD)/$(TARGET).bin
|
||||||
|
|
||||||
|
$(BUILD_FW)/firmware/vendorheader.o: $(SRCDIR_FW)/firmware/vendorheader.bin
|
||||||
|
$(Q)$(OBJCOPY) -I binary -O elf32-littlearm -B arm \
|
||||||
|
--rename-section .data=.vendorheader,alloc,load,readonly,contents \
|
||||||
|
$< $@
|
||||||
|
|
||||||
$(BUILD)/$(TARGET).elf: $(OBJ)
|
$(BUILD)/$(TARGET).elf: $(OBJ)
|
||||||
$(ECHO) "LINK $@"
|
$(ECHO) "LINK $@"
|
||||||
$(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
|
$(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||||
|
@ -61,12 +61,12 @@ TREZOR Core firmware consists of 3 parts:
|
|||||||
|
|
||||||
### Vendor Header
|
### Vendor Header
|
||||||
|
|
||||||
Total length of vendor header is 84 + 32 * (number of pubkeys) + (length of vendor string) + (length of vendor image) bytes rounded up to the closest multiply of 512 bytes.
|
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.
|
||||||
|
|
||||||
| offset | length | name | description |
|
| offset | length | name | description |
|
||||||
|-------:|-------:|------|-------------|
|
|-------:|-------:|------|-------------|
|
||||||
| 0x0000 | 4 | magic | firmware magic `TRZV` |
|
| 0x0000 | 4 | magic | firmware magic `TRZV` |
|
||||||
| 0x0004 | 4 | hdrlen | length of the vendor header |
|
| 0x0004 | 4 | hdrlen | length of the vendor header (multiple of 512) |
|
||||||
| 0x0008 | 4 | expiry | valid until timestamp (0=infinity) |
|
| 0x0008 | 4 | expiry | valid until timestamp (0=infinity) |
|
||||||
| 0x000C | 1 | vmajor | version (major) |
|
| 0x000C | 1 | vmajor | version (major) |
|
||||||
| 0x000D | 1 | vminor | version (minor) |
|
| 0x000D | 1 | vminor | version (minor) |
|
||||||
@ -77,8 +77,9 @@ Total length of vendor header is 84 + 32 * (number of pubkeys) + (length of vend
|
|||||||
| ? | 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 |
|
||||||
| ? | 2 | vimg_len | vendor image length |
|
| ? | ? | vstrpad | padding to a multiple of 4 bytes |
|
||||||
| ? | ? | vimg | vendor image (in [TOIf format](toif.md)) |
|
| ? | ? | vimg | vendor image (in [TOIf format](toif.md)) |
|
||||||
|
| ? | ? | reserved | padding to an address that is -65 modulo 512 |
|
||||||
| ? | 1 | sigmask | SatoshiLabs signature indexes (bitmap) |
|
| ? | 1 | sigmask | SatoshiLabs signature indexes (bitmap) |
|
||||||
| ? | 64 | sig | SatoshiLabs aggregated signature |
|
| ? | 64 | sig | SatoshiLabs aggregated signature |
|
||||||
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
#define IMAGE_MAGIC 0x4C5A5254 // TRZL
|
#define IMAGE_MAGIC 0x4C5A5254 // TRZL
|
||||||
#define IMAGE_MAXSIZE (1 * 64 * 1024 + 7 * 128 + 1024)
|
#define IMAGE_MAXSIZE (1 * 64 * 1024 + 7 * 128 * 1024)
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|
||||||
SCB->VTOR = FIRMWARE_START + HEADER_SIZE;
|
|
||||||
periph_init();
|
periph_init();
|
||||||
|
|
||||||
pendsv_init();
|
pendsv_init();
|
||||||
|
@ -28,6 +28,7 @@ SECTIONS
|
|||||||
/* Firmware Header */
|
/* Firmware Header */
|
||||||
.header :
|
.header :
|
||||||
{
|
{
|
||||||
|
KEEP(*(.vendorheader))
|
||||||
KEEP(*(.header))
|
KEEP(*(.header))
|
||||||
} > FLASH
|
} > FLASH
|
||||||
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
#define IMAGE_MAGIC 0x465A5254 // TRZF
|
#define IMAGE_MAGIC 0x465A5254 // TRZF
|
||||||
#define IMAGE_MAXSIZE (7 * 128 + 1024)
|
#define IMAGE_MAXSIZE (7 * 128 * 1024)
|
||||||
|
@ -17,14 +17,16 @@ void pendsv_isr_handler(void) {
|
|||||||
void check_and_jump(void)
|
void check_and_jump(void)
|
||||||
{
|
{
|
||||||
LOADER_PRINTLN("checking firmware");
|
LOADER_PRINTLN("checking firmware");
|
||||||
if (image_check_signature((const uint8_t *)FIRMWARE_START)) {
|
// TODO: check vendor header and its signature
|
||||||
|
uint32_t vhdrlen = *((const uint32_t *) (FIRMWARE_START + 4));
|
||||||
|
if (image_check_signature((const uint8_t *) (FIRMWARE_START + vhdrlen))) {
|
||||||
LOADER_PRINTLN("valid firmware image");
|
LOADER_PRINTLN("valid firmware image");
|
||||||
// TODO: remove debug wait
|
// TODO: remove debug wait
|
||||||
LOADER_PRINTLN("waiting 1 second");
|
LOADER_PRINTLN("waiting 1 second");
|
||||||
HAL_Delay(1000);
|
HAL_Delay(1000);
|
||||||
// end
|
// end
|
||||||
LOADER_PRINTLN("JUMP!");
|
LOADER_PRINTLN("JUMP!");
|
||||||
jump_to(FIRMWARE_START + HEADER_SIZE);
|
jump_to(FIRMWARE_START + vhdrlen + HEADER_SIZE);
|
||||||
} else {
|
} else {
|
||||||
LOADER_PRINTLN("invalid firmware image");
|
LOADER_PRINTLN("invalid firmware image");
|
||||||
}
|
}
|
||||||
|
35
tools/binctl
35
tools/binctl
@ -85,9 +85,15 @@ class BinImage:
|
|||||||
|
|
||||||
class FirmwareImage(BinImage):
|
class FirmwareImage(BinImage):
|
||||||
|
|
||||||
def __init__(self, data):
|
def __init__(self, data, vhdrlen):
|
||||||
super().__init__(data, magic=b'TRZF', max_size=7*128*1024)
|
super().__init__(data[vhdrlen:], magic=b'TRZF', max_size=7*128*1024)
|
||||||
|
self.vheader = data[0:vhdrlen]
|
||||||
|
|
||||||
|
def write(self, filename):
|
||||||
|
with open(filename, 'wb') as f:
|
||||||
|
f.write(self.vheader)
|
||||||
|
f.write(self.serialize_header())
|
||||||
|
f.write(self.code)
|
||||||
|
|
||||||
class LoaderImage(BinImage):
|
class LoaderImage(BinImage):
|
||||||
|
|
||||||
@ -95,7 +101,6 @@ class LoaderImage(BinImage):
|
|||||||
super().__init__(data, magic=b'TRZL', max_size=64*1024 + 7*128*1024)
|
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):
|
||||||
@ -107,7 +112,7 @@ class VendorHeader:
|
|||||||
self.vminor, \
|
self.vminor, \
|
||||||
self.vsig_m, \
|
self.vsig_m, \
|
||||||
self.vsig_n = header
|
self.vsig_n = header
|
||||||
assert self.magic == b'TRZF'
|
assert self.magic == b'TRZV'
|
||||||
assert self.vsig_m > 0 and self.vsig_m <= self.vsig_n
|
assert self.vsig_m > 0 and self.vsig_m <= self.vsig_n
|
||||||
assert self.vsig_n > 0 and self.vsig_n <= 8
|
assert self.vsig_n > 0 and self.vsig_n <= 8
|
||||||
p = 16
|
p = 16
|
||||||
@ -119,8 +124,9 @@ class VendorHeader:
|
|||||||
p += 1
|
p += 1
|
||||||
self.vstr = data[p:p + self.vstr_len]
|
self.vstr = data[p:p + self.vstr_len]
|
||||||
p += self.vstr_len
|
p += self.vstr_len
|
||||||
self.vimg_len, _ = struct.unpack('<H', data[p: p + 2])
|
vstr_pad = -p & 3
|
||||||
p += 2
|
p += vstr_pad
|
||||||
|
self.vimg_len = len(data) - 65 - p
|
||||||
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.sigmask = data[p]
|
self.sigmask = data[p]
|
||||||
@ -128,8 +134,8 @@ class VendorHeader:
|
|||||||
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 + \
|
||||||
32 * len(self.vpub) + \
|
32 * len(self.vpub) + \
|
||||||
1 + self.vstr_len + \
|
1 + self.vstr_len + vstr_pad + \
|
||||||
2 + self.vimg_len + \
|
self.vimg_len + \
|
||||||
1 + 64
|
1 + 64
|
||||||
|
|
||||||
def print(self):
|
def print(self):
|
||||||
@ -169,7 +175,6 @@ class VendorHeader:
|
|||||||
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())
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def binopen(filename):
|
def binopen(filename):
|
||||||
@ -180,13 +185,17 @@ 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':
|
||||||
|
vheader = VendorHeader(data)
|
||||||
|
if len(data) == vheader.hdrlen:
|
||||||
|
return vheader
|
||||||
|
subdata = data[vheader.hdrlen:]
|
||||||
|
if subdata[:4] == b'TRZF':
|
||||||
|
return FirmwareImage(data, vheader.hdrlen)
|
||||||
if magic == b'TRZF':
|
if magic == b'TRZF':
|
||||||
return FirmwareImage(data)
|
return FirmwareImage(data, 0)
|
||||||
# if magic == b'TRZV':
|
|
||||||
# return VendorHeader(data)
|
|
||||||
raise Exception('Unknown file format')
|
raise Exception('Unknown file format')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print('Usage: binctl file.bin [-s]')
|
print('Usage: binctl file.bin [-s]')
|
||||||
|
109
tools/build_vendorheader
Executable file
109
tools/build_vendorheader
Executable file
@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from PIL import Image
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import struct
|
||||||
|
import zlib
|
||||||
|
import binascii
|
||||||
|
|
||||||
|
def process_rgb(w, h, pix):
|
||||||
|
data = bytes()
|
||||||
|
for j in range(h):
|
||||||
|
for i in range(w):
|
||||||
|
r, g, b = pix[i, j]
|
||||||
|
c = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3)
|
||||||
|
data += struct.pack('>H', c)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def process_grayscale(w, h, pix):
|
||||||
|
data = bytes()
|
||||||
|
for j in range(h):
|
||||||
|
for i in range(w // 2):
|
||||||
|
l1, l2 = pix[i * 2, j], pix[i * 2 + 1, j]
|
||||||
|
c = (l1 & 0xF0) | (l2 >> 4)
|
||||||
|
data += struct.pack('>B', c)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def process_image(ifn):
|
||||||
|
im = Image.open(ifn)
|
||||||
|
w, h = im.size
|
||||||
|
print('Opened %s ... %d x %d @ %s' % (ifn, w, h, im.mode))
|
||||||
|
|
||||||
|
if im.mode == 'RGB':
|
||||||
|
print('Detected RGB mode')
|
||||||
|
elif im.mode == 'L':
|
||||||
|
if w % 2 > 0:
|
||||||
|
print('PNG file must have width divisible by 2')
|
||||||
|
return 3
|
||||||
|
print('Detected GRAYSCALE mode')
|
||||||
|
else:
|
||||||
|
print('Unknown mode:', im.mode)
|
||||||
|
return 4
|
||||||
|
|
||||||
|
pix = im.load()
|
||||||
|
|
||||||
|
if im.mode == 'RGB':
|
||||||
|
ofn = '%s.toif' % ifn[:-4]
|
||||||
|
pixeldata = process_rgb(w, h, pix)
|
||||||
|
else:
|
||||||
|
ofn = '%s.toig' % ifn[:-4]
|
||||||
|
pixeldata = process_grayscale(w, h, pix)
|
||||||
|
z = zlib.compressobj(level=9, wbits=10)
|
||||||
|
zdata = z.compress(pixeldata) + z.flush()
|
||||||
|
zdata = zdata[2:-4] # strip header and checksum
|
||||||
|
|
||||||
|
toif = b''
|
||||||
|
if im.mode == 'RGB':
|
||||||
|
toif += b'TOIf'
|
||||||
|
else:
|
||||||
|
toif += b'TOIg'
|
||||||
|
toif += struct.pack('<HH', w, h)
|
||||||
|
toif += struct.pack('<I', len(zdata))
|
||||||
|
toif += zdata
|
||||||
|
return toif
|
||||||
|
|
||||||
|
# encode vendor name, add length byte and padding to multiple of 4
|
||||||
|
def vendorencode(vname):
|
||||||
|
vbin = vname.encode('utf-8')
|
||||||
|
vbin = struct.pack('<B',len(vbin)) + vbin
|
||||||
|
vbin += b'\0' * (-len(vbin) & 3)
|
||||||
|
return vbin
|
||||||
|
|
||||||
|
def encodekey(key):
|
||||||
|
if len(key) != 64:
|
||||||
|
raise Exception("Wrong key length")
|
||||||
|
return binascii.unhexlify(key)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 7:
|
||||||
|
print('Usage build_vendorheader key1hex,... m version vendorname vendorimage.png vendorimage.bin')
|
||||||
|
return 1
|
||||||
|
|
||||||
|
keys = list(map(encodekey, sys.argv[1].split(',')))
|
||||||
|
m = int(sys.argv[2])
|
||||||
|
(major,minor) = map(lambda x: int(x),
|
||||||
|
re.search('^(\d+)\.(\d+)$', sys.argv[3]).groups())
|
||||||
|
vname = sys.argv[4]
|
||||||
|
ifn = sys.argv[5]
|
||||||
|
ofn = sys.argv[6]
|
||||||
|
if not ifn.endswith('.png'):
|
||||||
|
print('Must provide PNG file')
|
||||||
|
return 2
|
||||||
|
|
||||||
|
timeout = 0
|
||||||
|
vheader = b'TRZV' + struct.pack('<IIBBBB', 0, timeout, major, minor, m, len(keys))
|
||||||
|
for k in keys:
|
||||||
|
vheader += k
|
||||||
|
vheader += vendorencode(vname) + process_image(ifn)
|
||||||
|
padding = 65 + (-len(vheader) - 65) & 511
|
||||||
|
vheader += b'\0' * padding
|
||||||
|
|
||||||
|
# put in length
|
||||||
|
vheader = vheader[0:4] + struct.pack('<I', len(vheader)) + vheader[8:]
|
||||||
|
|
||||||
|
with open(ofn, 'wb') as f:
|
||||||
|
f.write(vheader)
|
||||||
|
|
||||||
|
main()
|
Loading…
Reference in New Issue
Block a user