From 9e82a4d34818221c94b9160fe8e45c1352c97141 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 26 Oct 2017 03:08:18 +0200 Subject: [PATCH] bootloader, firmware: introduce chunk hashes in the image header --- SConscript.bootloader | 4 ++- SConscript.firmware | 5 +-- docs/bootloader.md | 20 ++++++++--- embed/bootloader/header.S | 4 ++- embed/bootloader/messages.c | 6 ++-- embed/firmware/header.S | 4 ++- embed/trezorhal/image.h | 2 ++ tools/binctl | 68 +++++++++++++++++++++++++++---------- 8 files changed, 81 insertions(+), 32 deletions(-) diff --git a/SConscript.bootloader b/SConscript.bootloader index 198ae22f91..c220323ee2 100644 --- a/SConscript.bootloader +++ b/SConscript.bootloader @@ -172,5 +172,7 @@ program_bin = env.Command( source=program_elf, action=[ '$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`', ], ) diff --git a/SConscript.firmware b/SConscript.firmware index 180b1ec855..3851b7862b 100644 --- a/SConscript.firmware +++ b/SConscript.firmware @@ -430,6 +430,7 @@ program_bin = env.Command( source=program_elf, action=[ '$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 -s 1:2 `$COMBINE_SIGN firmware build/firmware/firmware.bin.novhdr 4747474747474747474747474747474747474747474747474747474747474747 4848484848484848484848484848484848484848484848484848484848484848`', + '$BINCTL $TARGET -h', + '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`', ], ) diff --git a/docs/bootloader.md b/docs/bootloader.md index 27310425b0..899c6643d7 100644 --- a/docs/bootloader.md +++ b/docs/bootloader.md @@ -79,9 +79,14 @@ Total length of bootloader header is always 1024 bytes. | 0x0011 | 1 | vminor | version (minor) | | 0x0012 | 1 | vpatch | version (patch) | | 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) | -| 0x03C0 | 64 | sig | SatoshiLabs aggregated signature | +| 0x03C0 | 64 | sig | SatoshiLabs aggregated signature of the bootloader header | ## Firmware Format @@ -118,7 +123,7 @@ of 512 bytes. | ? | ? | vimg | vendor image (in [TOIf format](toif.md)) | | ? | ? | reserved | padding to an address that is -65 modulo 512 (zeroed) | | ? | 1 | sigmask | SatoshiLabs signature indexes (bitmap) | -| ? | 64 | sig | SatoshiLabs aggregated signature | +| ? | 64 | sig | SatoshiLabs aggregated signature of the vendor header | ### Firmware Header @@ -134,9 +139,14 @@ Total length of firmware header is always 1024 bytes. | 0x0011 | 1 | vminor | version (minor) | | 0x0012 | 1 | vpatch | version (patch) | | 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) | -| 0x03C0 | 64 | sig | vendor aggregated signature | +| 0x03C0 | 64 | sig | vendor aggregated signature of the firmware header | ## Various ideas diff --git a/embed/bootloader/header.S b/embed/bootloader/header.S index 28890d733a..60a1e121ce 100644 --- a/embed/bootloader/header.S +++ b/embed/bootloader/header.S @@ -16,7 +16,9 @@ g_header: .byte VERSION_MINOR // vminor .byte VERSION_PATCH // vpatch .byte VERSION_BUILD // vbuild - . = . + 939 // reserved + . = . + 12 // reserved + . = . + 512 // hash1 ... hash16 + . = . + 415 // reserved .byte 0 // sigmask . = . + 64 // sig g_header_end: diff --git a/embed/bootloader/messages.c b/embed/bootloader/messages.c index cf1519bee3..b7c43934bc 100644 --- a/embed/bootloader/messages.c +++ b/embed/bootloader/messages.c @@ -15,8 +15,6 @@ #include "messages.h" #include "style.h" -#define FIRMWARE_CHUNK_SIZE (128 * 1024) - #define MSG_HEADER1_LEN 9 #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; } // 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_ASSIGN_VALUE(offset, 0); 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; 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_ASSIGN_VALUE(offset, firmware_flashed); MSG_SEND_ASSIGN_VALUE(length, chunk_requested); diff --git a/embed/firmware/header.S b/embed/firmware/header.S index c0da5192ba..5f30398692 100644 --- a/embed/firmware/header.S +++ b/embed/firmware/header.S @@ -16,7 +16,9 @@ g_header: .byte VERSION_MINOR // vminor .byte VERSION_PATCH // vpatch .byte VERSION_BUILD // vbuild - . = . + 939 // reserved + . = . + 12 // reserved + . = . + 512 // hash1 ... hash16 + . = . + 415 // reserved .byte 0 // sigmask . = . + 64 // sig g_header_end: diff --git a/embed/trezorhal/image.h b/embed/trezorhal/image.h index f891ea5d68..1923f6d8a0 100644 --- a/embed/trezorhal/image.h +++ b/embed/trezorhal/image.h @@ -7,8 +7,10 @@ #define BOARDLOADER_START 0x08000000 #define BOOTLOADER_START 0x08020000 #define FIRMWARE_START 0x08040000 + #define IMAGE_HEADER_SIZE 0x400 #define IMAGE_SIG_SIZE 65 +#define IMAGE_CHUNK_SIZE (128 * 1024) typedef struct { uint32_t magic; diff --git a/tools/binctl b/tools/binctl index 3261798bd9..b9ebcddffb 100755 --- a/tools/binctl +++ b/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 -IMAGE_HEADER_SIZE = 0x400 +IMAGE_HEADER_SIZE = 1024 IMAGE_SIG_SIZE = 65 +IMAGE_CHUNK_SIZE = 128 * 1024 class BinImage(object): 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.hdrlen, \ self.expiry, \ @@ -34,7 +35,9 @@ class BinImage(object): self.vminor, \ self.vpatch, \ self.vbuild, \ - self.reserved, \ + self.reserved1, \ + self.hashes, \ + self.reserved2, \ self.sigmask, \ self.sig = header assert self.magic == magic @@ -43,7 +46,8 @@ class BinImage(object): assert total_len % 512 == 0 assert total_len >= 4 * 1024 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:] assert len(self.code) == self.codelen @@ -56,21 +60,45 @@ class BinImage(object): total_len = self.hdrlen + self.codelen else: print('TREZOR Unknown Image') - print(' * magic :', self.magic.decode('ascii')) + print(' * magic :', self.magic.decode()) 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(' * 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(' * sig :', binascii.hexlify(self.sig).decode('ascii')) + print(' * sig :', binascii.hexlify(self.sig).decode()) print(' * total : %d bytes' % total_len) 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): - header = struct.pack('<4sIIIBBBB939s', + header = struct.pack('<4sIIIBBBB12s512s415s', self.magic, self.hdrlen, self.expiry, self.codelen, self.vmajor, self.vminor, self.vpatch, self.vbuild, - self.reserved) + self.reserved1, self.hashes, self.reserved2) if sig: header += struct.pack(' 0: pk = [vheader.vpub[i] for i in range(8) if firmware.sigmask & (1 << i)] global_pk = ed25519cosi.combine_keys(pk) - subdata_nosig = bytearray(subdata) - subdata_nosig[IMAGE_HEADER_SIZE - IMAGE_SIG_SIZE:IMAGE_HEADER_SIZE] = IMAGE_SIG_SIZE * b'\x00' - digest = pyblake2.blake2s(subdata_nosig).digest() + hdr = subdata[:IMAGE_HEADER_SIZE - IMAGE_SIG_SIZE] + IMAGE_SIG_SIZE * b'\x00' + digest = pyblake2.blake2s(hdr).digest() try: ed25519raw.checkvalid(firmware.sig, digest, global_pk) print('Firmware signature OK') @@ -232,10 +260,11 @@ def binopen(filename): def main(): if len(sys.argv) < 2: - print('Usage: binctl file.bin [-s sigmask signature]') + print('Usage: binctl file.bin [-s sigmask signature] [-h]') return 1 fn = sys.argv[1] sign = len(sys.argv) > 2 and sys.argv[2] == '-s' + rehash = len(sys.argv) == 3 and sys.argv[2] == '-h' b = binopen(fn) if sign: sigmask = 0 @@ -248,6 +277,9 @@ def main(): b.sign(sigmask, signature) print() b.write(fn) + if rehash: + b.update_hashes() + b.write(fn) b.print()