mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-19 14:08:11 +00:00
e0fd890661
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.
217 lines
6.7 KiB
Python
Executable File
217 lines
6.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import sys
|
|
import struct
|
|
import binascii
|
|
import ed25519
|
|
import pyblake2
|
|
|
|
|
|
def get_sig(data):
|
|
print('Enter privkey: ', end='')
|
|
seckey = binascii.unhexlify(input())
|
|
signkey = ed25519.SigningKey(seckey)
|
|
digest = pyblake2.blake2s(data).digest()
|
|
sigmask = 0x01 # (1 _ _ _ _)
|
|
sig = signkey.sign(digest)
|
|
return sigmask, sig
|
|
|
|
|
|
# loader/firmware headers specification: https://github.com/trezor/trezor-core/blob/master/docs/bootloader.md
|
|
|
|
|
|
class BinImage:
|
|
|
|
def __init__(self, data, magic, max_size):
|
|
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.sigmask, \
|
|
self.sig = header
|
|
assert self.magic == magic
|
|
assert self.hdrlen == 512
|
|
total_len = self.hdrlen + self.codelen
|
|
assert total_len % 512 == 0
|
|
assert total_len >= 4 * 1024
|
|
assert total_len <= max_size
|
|
assert self.reserved == 427 * b'\x00'
|
|
self.code = data[self.hdrlen:]
|
|
assert len(self.code) == self.codelen
|
|
|
|
def print(self):
|
|
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(' * 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(' * sigmask :', self.sigmask)
|
|
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.sigmask, 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.sigmask, self.sig = get_sig(data)
|
|
|
|
def write(self, filename):
|
|
with open(filename, 'wb') as f:
|
|
f.write(self.serialize_header())
|
|
f.write(self.code)
|
|
|
|
|
|
class FirmwareImage(BinImage):
|
|
|
|
def __init__(self, data, vhdrlen):
|
|
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):
|
|
|
|
def __init__(self, data):
|
|
super().__init__(data, magic=b'TRZL', max_size=64*1024 + 7*128*1024)
|
|
|
|
|
|
class VendorHeader:
|
|
|
|
def __init__(self, data):
|
|
header = struct.unpack('<4sIIBBBB', data[:16])
|
|
self.magic, \
|
|
self.hdrlen, \
|
|
self.expiry, \
|
|
self.vmajor, \
|
|
self.vminor, \
|
|
self.vsig_m, \
|
|
self.vsig_n = header
|
|
assert self.magic == b'TRZV'
|
|
assert self.vsig_m > 0 and self.vsig_m <= self.vsig_n
|
|
assert self.vsig_n > 0 and self.vsig_n <= 8
|
|
p = 16
|
|
self.vpub = []
|
|
for _ in range(self.vsig_n):
|
|
self.vpub.append(data[p:p + 32])
|
|
p += 32
|
|
self.vstr_len = data[p]
|
|
p += 1
|
|
self.vstr = data[p:p + self.vstr_len]
|
|
p += self.vstr_len
|
|
vstr_pad = -p & 3
|
|
p += vstr_pad
|
|
self.vimg_len = len(data) - 65 - p
|
|
self.vimg = data[p:p + self.vimg_len]
|
|
p += self.vimg_len
|
|
self.sigmask = data[p]
|
|
p += 1
|
|
self.sig = data[p:p+64]
|
|
assert len(data) == 4 + 4 + 4 + 1 + 1 + 1 + 1 + \
|
|
32 * len(self.vpub) + \
|
|
1 + self.vstr_len + vstr_pad + \
|
|
self.vimg_len + \
|
|
1 + 64
|
|
|
|
def print(self):
|
|
print('TREZOR Vendor Header')
|
|
print(' * magic :', self.magic.decode('ascii'))
|
|
print(' * hdrlen :', self.hdrlen)
|
|
print(' * expiry :', self.expiry)
|
|
print(' * version : %d.%d' % (self.vmajor, self.vminor))
|
|
print(' * scheme : %d out of %d' % (self.vsig_m, self.vsig_n))
|
|
for i in range(self.vsig_n):
|
|
print(' * vpub #%d :' % (i + 1), binascii.hexlify(self.vpub[i]).decode('ascii'))
|
|
print(' * vstr :', self.vstr.decode('ascii'))
|
|
print(' * vimg : (%d bytes)', len(self.vimg))
|
|
print(' * sigmask :', self.sigmask)
|
|
print(' * sig :', binascii.hexlify(self.sig).decode('ascii'))
|
|
|
|
def serialize_header(self, sig=True):
|
|
header = struct.pack('<4sIIBBBB', \
|
|
self.magic, self.hdrlen, self.expiry, \
|
|
self.vmajor, self.vminor, \
|
|
self.vsig_m, self.vsig_n)
|
|
for i in range(self.vsig_n):
|
|
header += self.vpub[i]
|
|
header += struct.pack('<B', self.vstr_len) + self.vstr
|
|
header += struct.pack('<H', self.vimg_len) + self.vimg
|
|
if sig:
|
|
header += struct.pack('<B64s', self.sigmask, self.sig)
|
|
else:
|
|
header += 65 * b'\x00'
|
|
assert len(header) == self.hdrlen
|
|
return header
|
|
|
|
def sign(self):
|
|
header = self.serialize_header(sig=False)
|
|
self.sigmask, self.sig = get_sig(header)
|
|
|
|
def write(self, filename):
|
|
with open(filename, 'wb') as f:
|
|
f.write(self.serialize_header())
|
|
|
|
|
|
def binopen(filename):
|
|
print()
|
|
print('Opening file', filename)
|
|
print()
|
|
data = open(filename, 'rb').read()
|
|
magic = data[:4]
|
|
if magic == b'TRZL':
|
|
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':
|
|
return FirmwareImage(data, 0)
|
|
raise Exception('Unknown file format')
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print('Usage: binctl file.bin [-s]')
|
|
return 1
|
|
fn = sys.argv[1]
|
|
sign = len(sys.argv) > 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()
|