From 7d4414000e25c42da3171f6aadd6fd3ff7c4bdc6 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 9 Feb 2017 16:26:15 +0100 Subject: [PATCH] tools: join check_bootloader and check_firmware into firmwarectl tool --- docs/bootloader.md | 28 ++++----- tools/check_bootloader | 38 ----------- tools/check_firmware | 17 ----- tools/firmwarectl | 139 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 69 deletions(-) delete mode 100755 tools/check_bootloader delete mode 100755 tools/check_firmware create mode 100755 tools/firmwarectl diff --git a/docs/bootloader.md b/docs/bootloader.md index 4bb3965f..bfa9f8a7 100644 --- a/docs/bootloader.md +++ b/docs/bootloader.md @@ -22,6 +22,7 @@ it will start in a firmware update mode, allowing a firmware update via USB. * 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. +* There is a tool called [firmwarectl](../tools/firmwarectl) which checks validity of the bootloader/firmware images including their headers. ##Bootloader Format @@ -30,11 +31,10 @@ 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. +Total length of bootloader header is always 256 bytes. | offset | length | name | description | |-------:|-------:|------|-------------| @@ -46,9 +46,9 @@ Total length of bootloader header is 256 bytes. | 0x0011 | 1 | vminor | version (minor) | | 0x0012 | 1 | vpatch | version (patch) | | 0x0013 | 1 | vbuild | version (build) | -| 0x0014 | 1 | sigidx | SatoshiLabs signature indexes (bitmap) | -| 0x0015 | 64 | sig | SatoshiLabs signature | -| 0x0055 | 171 | reserved | not used yet (zeroed) | +| 0x0014 | 171 | reserved | not used yet (zeroed) | +| 0x00BF | 1 | sigidx | SatoshiLabs signature indexes (bitmap) | +| 0x00C0 | 64 | sig | SatoshiLabs signature | ##Firmware Format @@ -58,8 +58,6 @@ 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. @@ -69,21 +67,23 @@ Total length of vendor header is 82 + 32 * (number of pubkeys) + (length of vend | 0x0000 | 4 | magic | firmware magic `TRZV` | | 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 (zeroed) | +| 0x000C | 1 | vmajor | version (major) | +| 0x000D | 1 | vminor | version (minor) | +| 0x000E | 1 | vsig_m | number of signatures needed to run the firmware from this vendor | +| 0x000F | 1 | vsig_n | number of different pubkeys vendor provides for signing | | 0x0010 | 32 | vpub1 | vendor pubkey 1 | | ... | ... | ... | ... | | ? | 32 | vpubn | vendor pubkey n | | ? | 1 | vstr_len | vendor string length | | ? | ? | vstr | vendor string | +| ? | 2 | vimg_len | vendor image length | | ? | ? | vimg | vendor image (in [TOIf format](toif.md)) | | ? | 1 | sigidx | SatoshiLabs signature indexes (bitmap) | | ? | 64 | sig | SatoshiLabs signature | ###Firmware Header -Total length of firmware header is 256 bytes. +Total length of firmware header is always 256 bytes. | offset | length | name | description | |-------:|-------:|------|-------------| @@ -95,9 +95,9 @@ Total length of firmware header is 256 bytes. | 0x0011 | 1 | vminor | version (minor) | | 0x0012 | 1 | vpatch | version (patch) | | 0x0013 | 1 | vbuild | version (build) | -| 0x0014 | 1 | sigidx | vendor signature indexes (bitmap) | -| 0x0015 | 64 | sig | vendor signature | -| 0x0055 | 171 | reserved | not used yet (zeroed) | +| 0x0014 | 171 | reserved | not used yet (zeroed) | +| 0x00BF | 1 | sigidx | vendor signature indexes (bitmap) | +| 0x00C0 | 64 | sig | vendor signature | ##Various ideas diff --git a/tools/check_bootloader b/tools/check_bootloader deleted file mode 100755 index 619a3573..00000000 --- a/tools/check_bootloader +++ /dev/null @@ -1,38 +0,0 @@ -#!/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 deleted file mode 100755 index 7412a123..00000000 --- a/tools/check_firmware +++ /dev/null @@ -1,17 +0,0 @@ -#!/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/firmwarectl b/tools/firmwarectl new file mode 100755 index 00000000..aefa85b5 --- /dev/null +++ b/tools/firmwarectl @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +import sys +import struct +import binascii +import hashlib + +import ed25519 + +# bootloader/firmware headers specification: https://github.com/trezor/trezor-core/blob/master/docs/bootloader.md + +# converts 8-bit bitmap to tuple of values +def bitmap_to_tuple(b): + return tuple([ i + 1 for i in range(8) if b & (1 << i) ]) + + +# converts tuple of values to 8-bit bitmap +def tuple_to_bitmap(t): + b = 0 + for i in t: + if i >= 1 and i <= 8: + b |= (1 << (i - 1)) + return b + + +def get_sig(data): + print('Enter privkey: ', end='') + seckey = binascii.unhexlify(input()) + signkey = ed25519.SigningKey(seckey) + digest = hashlib.sha256(data).digest() + sigidx = (1, ) + sig = signkey.sign(digest) + return sigidx, sig + + +class BootloaderImage: + + def __init__(self, data): + header = struct.unpack('<4sIIIBBBB171sB64s', data[:256]) + 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'TRZB' + assert self.hdrlen == 256 + assert self.codelen == 64*1024 + assert self.reserved == 171 * b'\x00' + self.sigidx = bitmap_to_tuple(self.sigidx) + self.code = data[256:] + assert len(self.code) == self.codelen + + def print(self): + print('TREZOR Bootloader 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 header(self, sig=True): + header = struct.pack('<4sIIIBBBB171s', \ + self.magic, self.hdrlen, self.expiry, self.codelen, \ + self.vmajor, self.vminor, self.vpatch, self.vbuild, \ + self.reserved) + if sig: + sigidx = tuple_to_bitmap(self.sigidx) + header += struct.pack(' 2 and sys.argv[2] == '-s' + b = binopen(fn) + b.print() + if sign: + print() + b.sign() + print() + b.print() + b.write(fn) + + +if __name__ == '__main__': + main()