mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-26 17:38:39 +00:00
bootloader, firmware: introduce chunk hashes in the image header
This commit is contained in:
parent
3c974095aa
commit
9e82a4d348
@ -172,5 +172,7 @@ program_bin = env.Command(
|
|||||||
source=program_elf,
|
source=program_elf,
|
||||||
action=[
|
action=[
|
||||||
'$OBJCOPY -O binary -j .header -j .flash -j .data $SOURCE $TARGET',
|
'$OBJCOPY -O binary -j .header -j .flash -j .data $SOURCE $TARGET',
|
||||||
'$BINCTL $TARGET -s 1:2 `$COMBINE_SIGN bootloader $TARGET 4141414141414141414141414141414141414141414141414141414141414141 4242424242424242424242424242424242424242424242424242424242424242`',
|
'$BINCTL $TARGET -h',
|
||||||
|
'dd if=$TARGET of=build/bootloader/header.tosign bs=1 count=1024',
|
||||||
|
'$BINCTL $TARGET -s 1:2 `$COMBINE_SIGN bootloader build/bootloader/header.tosign 4141414141414141414141414141414141414141414141414141414141414141 4242424242424242424242424242424242424242424242424242424242424242`',
|
||||||
], )
|
], )
|
||||||
|
@ -430,6 +430,7 @@ program_bin = env.Command(
|
|||||||
source=program_elf,
|
source=program_elf,
|
||||||
action=[
|
action=[
|
||||||
'$OBJCOPY -O binary -j .vendorheader -j .header -j .flash -j .data $SOURCE $TARGET',
|
'$OBJCOPY -O binary -j .vendorheader -j .header -j .flash -j .data $SOURCE $TARGET',
|
||||||
'$OBJCOPY -O binary -j .header -j .flash -j .data $SOURCE build/firmware/firmware.bin.novhdr',
|
'$BINCTL $TARGET -h',
|
||||||
'$BINCTL $TARGET -s 1:2 `$COMBINE_SIGN firmware build/firmware/firmware.bin.novhdr 4747474747474747474747474747474747474747474747474747474747474747 4848484848484848484848484848484848484848484848484848484848484848`',
|
'dd if=$TARGET of=build/firmware/header.tosign bs=1 count=1024 skip=`stat -c%s embed/firmware/vendorheader.bin`',
|
||||||
|
'$BINCTL $TARGET -s 1:2 `$COMBINE_SIGN firmware build/firmware/header.tosign 4747474747474747474747474747474747474747474747474747474747474747 4848484848484848484848484848484848484848484848484848484848484848`',
|
||||||
], )
|
], )
|
||||||
|
@ -79,9 +79,14 @@ Total length of bootloader header is always 1024 bytes.
|
|||||||
| 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 | 939 | reserved | not used yet (zeroed) |
|
| 0x0014 | 12 | reserved | not used yet (zeroed) |
|
||||||
|
| 0x0020 | 32 | hash1 | hash of the first code chunk (128 - 1 KiB), this excludes the header |
|
||||||
|
| 0x0040 | 32 | hash2 | hash of the second code chunk (128 KiB), zeroed if unused |
|
||||||
|
| ... | ... | ... | ... |
|
||||||
|
| 0x0200 | 32 | hash16 | hash of the last possible code chunk (128 KiB), zeroed if unused |
|
||||||
|
| 0x0220 | 415 | reserved | not used yet (zeroed) |
|
||||||
| 0x03BF | 1 | sigmask | SatoshiLabs signature indexes (bitmap) |
|
| 0x03BF | 1 | sigmask | SatoshiLabs signature indexes (bitmap) |
|
||||||
| 0x03C0 | 64 | sig | SatoshiLabs aggregated signature |
|
| 0x03C0 | 64 | sig | SatoshiLabs aggregated signature of the bootloader header |
|
||||||
|
|
||||||
## Firmware Format
|
## Firmware Format
|
||||||
|
|
||||||
@ -118,7 +123,7 @@ of 512 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 (zeroed) |
|
| ? | ? | reserved | padding to an address that is -65 modulo 512 (zeroed) |
|
||||||
| ? | 1 | sigmask | SatoshiLabs signature indexes (bitmap) |
|
| ? | 1 | sigmask | SatoshiLabs signature indexes (bitmap) |
|
||||||
| ? | 64 | sig | SatoshiLabs aggregated signature |
|
| ? | 64 | sig | SatoshiLabs aggregated signature of the vendor header |
|
||||||
|
|
||||||
### Firmware Header
|
### Firmware Header
|
||||||
|
|
||||||
@ -134,9 +139,14 @@ Total length of firmware header is always 1024 bytes.
|
|||||||
| 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 | 939 | reserved | not used yet (zeroed) |
|
| 0x0014 | 12 | reserved | not used yet (zeroed) |
|
||||||
|
| 0x0020 | 32 | hash1 | hash of the first code chunk (128 - 1 KiB), this excludes the header |
|
||||||
|
| 0x0040 | 32 | hash2 | hash of the second code chunk (128 KiB), zeroed if unused |
|
||||||
|
| ... | ... | ... | ... |
|
||||||
|
| 0x0200 | 32 | hash16 | hash of the last possible code chunk (128 KiB), zeroed if unused |
|
||||||
|
| 0x0220 | 415 | reserved | not used yet (zeroed) |
|
||||||
| 0x03BF | 1 | sigmask | vendor signature indexes (bitmap) |
|
| 0x03BF | 1 | sigmask | vendor signature indexes (bitmap) |
|
||||||
| 0x03C0 | 64 | sig | vendor aggregated signature |
|
| 0x03C0 | 64 | sig | vendor aggregated signature of the firmware header |
|
||||||
|
|
||||||
|
|
||||||
## Various ideas
|
## Various ideas
|
||||||
|
@ -16,7 +16,9 @@ g_header:
|
|||||||
.byte VERSION_MINOR // vminor
|
.byte VERSION_MINOR // vminor
|
||||||
.byte VERSION_PATCH // vpatch
|
.byte VERSION_PATCH // vpatch
|
||||||
.byte VERSION_BUILD // vbuild
|
.byte VERSION_BUILD // vbuild
|
||||||
. = . + 939 // reserved
|
. = . + 12 // reserved
|
||||||
|
. = . + 512 // hash1 ... hash16
|
||||||
|
. = . + 415 // reserved
|
||||||
.byte 0 // sigmask
|
.byte 0 // sigmask
|
||||||
. = . + 64 // sig
|
. = . + 64 // sig
|
||||||
g_header_end:
|
g_header_end:
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
#include "messages.h"
|
#include "messages.h"
|
||||||
#include "style.h"
|
#include "style.h"
|
||||||
|
|
||||||
#define FIRMWARE_CHUNK_SIZE (128 * 1024)
|
|
||||||
|
|
||||||
#define MSG_HEADER1_LEN 9
|
#define MSG_HEADER1_LEN 9
|
||||||
#define MSG_HEADER2_LEN 1
|
#define MSG_HEADER2_LEN 1
|
||||||
|
|
||||||
@ -270,7 +268,7 @@ void process_msg_FirmwareErase(uint8_t iface_num, uint32_t msg_size, uint8_t *bu
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// request new firmware
|
// request new firmware
|
||||||
chunk_requested = (firmware_remaining > FIRMWARE_CHUNK_SIZE) ? FIRMWARE_CHUNK_SIZE : firmware_remaining;
|
chunk_requested = (firmware_remaining > IMAGE_CHUNK_SIZE) ? IMAGE_CHUNK_SIZE : firmware_remaining;
|
||||||
MSG_SEND_INIT(FirmwareRequest);
|
MSG_SEND_INIT(FirmwareRequest);
|
||||||
MSG_SEND_ASSIGN_VALUE(offset, 0);
|
MSG_SEND_ASSIGN_VALUE(offset, 0);
|
||||||
MSG_SEND_ASSIGN_VALUE(length, chunk_requested);
|
MSG_SEND_ASSIGN_VALUE(length, chunk_requested);
|
||||||
@ -336,7 +334,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, uint8_t *bu
|
|||||||
firmware_flashed += chunk_requested;
|
firmware_flashed += chunk_requested;
|
||||||
|
|
||||||
if (firmware_remaining > 0) {
|
if (firmware_remaining > 0) {
|
||||||
chunk_requested = (firmware_remaining > FIRMWARE_CHUNK_SIZE) ? FIRMWARE_CHUNK_SIZE : firmware_remaining;
|
chunk_requested = (firmware_remaining > IMAGE_CHUNK_SIZE) ? IMAGE_CHUNK_SIZE : firmware_remaining;
|
||||||
MSG_SEND_INIT(FirmwareRequest);
|
MSG_SEND_INIT(FirmwareRequest);
|
||||||
MSG_SEND_ASSIGN_VALUE(offset, firmware_flashed);
|
MSG_SEND_ASSIGN_VALUE(offset, firmware_flashed);
|
||||||
MSG_SEND_ASSIGN_VALUE(length, chunk_requested);
|
MSG_SEND_ASSIGN_VALUE(length, chunk_requested);
|
||||||
|
@ -16,7 +16,9 @@ g_header:
|
|||||||
.byte VERSION_MINOR // vminor
|
.byte VERSION_MINOR // vminor
|
||||||
.byte VERSION_PATCH // vpatch
|
.byte VERSION_PATCH // vpatch
|
||||||
.byte VERSION_BUILD // vbuild
|
.byte VERSION_BUILD // vbuild
|
||||||
. = . + 939 // reserved
|
. = . + 12 // reserved
|
||||||
|
. = . + 512 // hash1 ... hash16
|
||||||
|
. = . + 415 // reserved
|
||||||
.byte 0 // sigmask
|
.byte 0 // sigmask
|
||||||
. = . + 64 // sig
|
. = . + 64 // sig
|
||||||
g_header_end:
|
g_header_end:
|
||||||
|
@ -7,8 +7,10 @@
|
|||||||
#define BOARDLOADER_START 0x08000000
|
#define BOARDLOADER_START 0x08000000
|
||||||
#define BOOTLOADER_START 0x08020000
|
#define BOOTLOADER_START 0x08020000
|
||||||
#define FIRMWARE_START 0x08040000
|
#define FIRMWARE_START 0x08040000
|
||||||
|
|
||||||
#define IMAGE_HEADER_SIZE 0x400
|
#define IMAGE_HEADER_SIZE 0x400
|
||||||
#define IMAGE_SIG_SIZE 65
|
#define IMAGE_SIG_SIZE 65
|
||||||
|
#define IMAGE_CHUNK_SIZE (128 * 1024)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
|
68
tools/binctl
68
tools/binctl
@ -18,14 +18,15 @@ def format_sigmask(sigmask):
|
|||||||
|
|
||||||
# bootloader/firmware headers specification: https://github.com/trezor/trezor-core/blob/master/docs/bootloader.md
|
# bootloader/firmware headers specification: https://github.com/trezor/trezor-core/blob/master/docs/bootloader.md
|
||||||
|
|
||||||
IMAGE_HEADER_SIZE = 0x400
|
IMAGE_HEADER_SIZE = 1024
|
||||||
IMAGE_SIG_SIZE = 65
|
IMAGE_SIG_SIZE = 65
|
||||||
|
IMAGE_CHUNK_SIZE = 128 * 1024
|
||||||
|
|
||||||
|
|
||||||
class BinImage(object):
|
class BinImage(object):
|
||||||
|
|
||||||
def __init__(self, data, magic, max_size):
|
def __init__(self, data, magic, max_size):
|
||||||
header = struct.unpack('<4sIIIBBBB939sB64s', data[:IMAGE_HEADER_SIZE])
|
header = struct.unpack('<4sIIIBBBB12s512s415sB64s', data[:IMAGE_HEADER_SIZE])
|
||||||
self.magic, \
|
self.magic, \
|
||||||
self.hdrlen, \
|
self.hdrlen, \
|
||||||
self.expiry, \
|
self.expiry, \
|
||||||
@ -34,7 +35,9 @@ class BinImage(object):
|
|||||||
self.vminor, \
|
self.vminor, \
|
||||||
self.vpatch, \
|
self.vpatch, \
|
||||||
self.vbuild, \
|
self.vbuild, \
|
||||||
self.reserved, \
|
self.reserved1, \
|
||||||
|
self.hashes, \
|
||||||
|
self.reserved2, \
|
||||||
self.sigmask, \
|
self.sigmask, \
|
||||||
self.sig = header
|
self.sig = header
|
||||||
assert self.magic == magic
|
assert self.magic == magic
|
||||||
@ -43,7 +46,8 @@ class BinImage(object):
|
|||||||
assert total_len % 512 == 0
|
assert total_len % 512 == 0
|
||||||
assert total_len >= 4 * 1024
|
assert total_len >= 4 * 1024
|
||||||
assert total_len <= max_size
|
assert total_len <= max_size
|
||||||
assert self.reserved == 939 * b'\x00'
|
assert self.reserved1 == 12 * b'\x00'
|
||||||
|
assert self.reserved2 == 415 * b'\x00'
|
||||||
self.code = data[self.hdrlen:]
|
self.code = data[self.hdrlen:]
|
||||||
assert len(self.code) == self.codelen
|
assert len(self.code) == self.codelen
|
||||||
|
|
||||||
@ -56,21 +60,45 @@ class BinImage(object):
|
|||||||
total_len = self.hdrlen + self.codelen
|
total_len = self.hdrlen + self.codelen
|
||||||
else:
|
else:
|
||||||
print('TREZOR Unknown Image')
|
print('TREZOR Unknown Image')
|
||||||
print(' * magic :', self.magic.decode('ascii'))
|
print(' * magic :', self.magic.decode())
|
||||||
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(' * hashes: %s' % ('OK' if self.check_hashes() else 'INCORRECT'))
|
||||||
|
for i in range(16):
|
||||||
|
print(' - %02d : %s' % (i, binascii.hexlify(self.hashes[i * 32:i * 32 + 32]).decode()))
|
||||||
print(' * sigmask :', format_sigmask(self.sigmask))
|
print(' * sigmask :', format_sigmask(self.sigmask))
|
||||||
print(' * sig :', binascii.hexlify(self.sig).decode('ascii'))
|
print(' * sig :', binascii.hexlify(self.sig).decode())
|
||||||
print(' * total : %d bytes' % total_len)
|
print(' * total : %d bytes' % total_len)
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
def compute_hashes(self):
|
||||||
|
hashes = b''
|
||||||
|
for i in range(16):
|
||||||
|
if i == 0:
|
||||||
|
d = self.code[:IMAGE_CHUNK_SIZE - IMAGE_HEADER_SIZE]
|
||||||
|
else:
|
||||||
|
s = IMAGE_CHUNK_SIZE - IMAGE_HEADER_SIZE + (i - 1) * IMAGE_CHUNK_SIZE
|
||||||
|
d = self.code[s:s + IMAGE_CHUNK_SIZE]
|
||||||
|
if len(d) > 0:
|
||||||
|
h = pyblake2.blake2s(d).digest()
|
||||||
|
else:
|
||||||
|
h = 32 * b'\x00'
|
||||||
|
hashes += h
|
||||||
|
return hashes
|
||||||
|
|
||||||
|
def check_hashes(self):
|
||||||
|
return self.hashes == self.compute_hashes()
|
||||||
|
|
||||||
|
def update_hashes(self):
|
||||||
|
self.hashes = self.compute_hashes()
|
||||||
|
|
||||||
def serialize_header(self, sig=True):
|
def serialize_header(self, sig=True):
|
||||||
header = struct.pack('<4sIIIBBBB939s',
|
header = struct.pack('<4sIIIBBBB12s512s415s',
|
||||||
self.magic, self.hdrlen, self.expiry, self.codelen,
|
self.magic, self.hdrlen, self.expiry, self.codelen,
|
||||||
self.vmajor, self.vminor, self.vpatch, self.vbuild,
|
self.vmajor, self.vminor, self.vpatch, self.vbuild,
|
||||||
self.reserved)
|
self.reserved1, self.hashes, self.reserved2)
|
||||||
if sig:
|
if sig:
|
||||||
header += struct.pack('<B64s', self.sigmask, self.sig)
|
header += struct.pack('<B64s', self.sigmask, self.sig)
|
||||||
else:
|
else:
|
||||||
@ -94,7 +122,7 @@ class BinImage(object):
|
|||||||
class FirmwareImage(BinImage):
|
class FirmwareImage(BinImage):
|
||||||
|
|
||||||
def __init__(self, data, vhdrlen):
|
def __init__(self, data, vhdrlen):
|
||||||
super(FirmwareImage, self).__init__(data[vhdrlen:], magic=b'TRZF', max_size=6 * 128 * 1024)
|
super(FirmwareImage, self).__init__(data[vhdrlen:], magic=b'TRZF', max_size=6 * IMAGE_CHUNK_SIZE)
|
||||||
self.vhdrlen = vhdrlen
|
self.vhdrlen = vhdrlen
|
||||||
self.vheader = data[:vhdrlen]
|
self.vheader = data[:vhdrlen]
|
||||||
|
|
||||||
@ -108,7 +136,7 @@ class FirmwareImage(BinImage):
|
|||||||
class BootloaderImage(BinImage):
|
class BootloaderImage(BinImage):
|
||||||
|
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
super(BootloaderImage, self).__init__(data, magic=b'TRZB', max_size=1 * 128 * 1024)
|
super(BootloaderImage, self).__init__(data, magic=b'TRZB', max_size=1 * IMAGE_CHUNK_SIZE)
|
||||||
|
|
||||||
|
|
||||||
class VendorHeader(object):
|
class VendorHeader(object):
|
||||||
@ -148,21 +176,22 @@ class VendorHeader(object):
|
|||||||
1 + self.vstr_len + vstr_pad + \
|
1 + self.vstr_len + vstr_pad + \
|
||||||
self.vimg_len + \
|
self.vimg_len + \
|
||||||
IMAGE_SIG_SIZE
|
IMAGE_SIG_SIZE
|
||||||
|
assert len(data) % 512 == 0
|
||||||
|
|
||||||
def print(self):
|
def print(self):
|
||||||
print('TREZOR Vendor Header')
|
print('TREZOR Vendor Header')
|
||||||
print(' * magic :', self.magic.decode('ascii'))
|
print(' * magic :', self.magic.decode())
|
||||||
print(' * hdrlen :', self.hdrlen)
|
print(' * hdrlen :', self.hdrlen)
|
||||||
print(' * expiry :', self.expiry)
|
print(' * expiry :', self.expiry)
|
||||||
print(' * version : %d.%d' % (self.vmajor, self.vminor))
|
print(' * version : %d.%d' % (self.vmajor, self.vminor))
|
||||||
print(' * scheme : %d out of %d' % (self.vsig_m, self.vsig_n))
|
print(' * scheme : %d out of %d' % (self.vsig_m, self.vsig_n))
|
||||||
print(' * trust :', self.vtrust)
|
print(' * trust :', self.vtrust)
|
||||||
for i in range(self.vsig_n):
|
for i in range(self.vsig_n):
|
||||||
print(' * vpub #%d :' % (i + 1), binascii.hexlify(self.vpub[i]).decode('ascii'))
|
print(' * vpub #%d :' % (i + 1), binascii.hexlify(self.vpub[i]).decode())
|
||||||
print(' * vstr :', self.vstr.decode('ascii'))
|
print(' * vstr :', self.vstr.decode())
|
||||||
print(' * vimg : (%d bytes)' % len(self.vimg))
|
print(' * vimg : (%d bytes)' % len(self.vimg))
|
||||||
print(' * sigmask :', format_sigmask(self.sigmask))
|
print(' * sigmask :', format_sigmask(self.sigmask))
|
||||||
print(' * sig :', binascii.hexlify(self.sig).decode('ascii'))
|
print(' * sig :', binascii.hexlify(self.sig).decode())
|
||||||
print()
|
print()
|
||||||
|
|
||||||
def serialize_header(self, sig=True):
|
def serialize_header(self, sig=True):
|
||||||
@ -214,9 +243,8 @@ def binopen(filename):
|
|||||||
if firmware.sigmask > 0:
|
if firmware.sigmask > 0:
|
||||||
pk = [vheader.vpub[i] for i in range(8) if firmware.sigmask & (1 << i)]
|
pk = [vheader.vpub[i] for i in range(8) if firmware.sigmask & (1 << i)]
|
||||||
global_pk = ed25519cosi.combine_keys(pk)
|
global_pk = ed25519cosi.combine_keys(pk)
|
||||||
subdata_nosig = bytearray(subdata)
|
hdr = subdata[:IMAGE_HEADER_SIZE - IMAGE_SIG_SIZE] + IMAGE_SIG_SIZE * b'\x00'
|
||||||
subdata_nosig[IMAGE_HEADER_SIZE - IMAGE_SIG_SIZE:IMAGE_HEADER_SIZE] = IMAGE_SIG_SIZE * b'\x00'
|
digest = pyblake2.blake2s(hdr).digest()
|
||||||
digest = pyblake2.blake2s(subdata_nosig).digest()
|
|
||||||
try:
|
try:
|
||||||
ed25519raw.checkvalid(firmware.sig, digest, global_pk)
|
ed25519raw.checkvalid(firmware.sig, digest, global_pk)
|
||||||
print('Firmware signature OK')
|
print('Firmware signature OK')
|
||||||
@ -232,10 +260,11 @@ def binopen(filename):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print('Usage: binctl file.bin [-s sigmask signature]')
|
print('Usage: binctl file.bin [-s sigmask signature] [-h]')
|
||||||
return 1
|
return 1
|
||||||
fn = sys.argv[1]
|
fn = sys.argv[1]
|
||||||
sign = len(sys.argv) > 2 and sys.argv[2] == '-s'
|
sign = len(sys.argv) > 2 and sys.argv[2] == '-s'
|
||||||
|
rehash = len(sys.argv) == 3 and sys.argv[2] == '-h'
|
||||||
b = binopen(fn)
|
b = binopen(fn)
|
||||||
if sign:
|
if sign:
|
||||||
sigmask = 0
|
sigmask = 0
|
||||||
@ -248,6 +277,9 @@ def main():
|
|||||||
b.sign(sigmask, signature)
|
b.sign(sigmask, signature)
|
||||||
print()
|
print()
|
||||||
b.write(fn)
|
b.write(fn)
|
||||||
|
if rehash:
|
||||||
|
b.update_hashes()
|
||||||
|
b.write(fn)
|
||||||
b.print()
|
b.print()
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user