2022-09-20 11:30:36 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
import sys
|
|
|
|
from hashlib import sha256
|
|
|
|
|
|
|
|
|
|
|
|
class Signatures:
|
|
|
|
# offsets from T1 firmware hash
|
|
|
|
sig_offsets = [544, 608, 672]
|
|
|
|
sigindex_offsets = [736, 737, 738]
|
|
|
|
signature_pairs = [] # list of tupes (int, bytes)
|
|
|
|
|
|
|
|
def __init__(self, filename):
|
|
|
|
"""Load FW, zero out signature fiels, compute header hash"""
|
|
|
|
self.fw_image = None # mutable bytearray
|
|
|
|
self.load_fw(filename)
|
|
|
|
self.header_hash = sha256(self.get_header()).digest()
|
|
|
|
print(f"Loaded FW image with header hash {self.header_hash_hex()}")
|
|
|
|
|
|
|
|
def load_fw(self, filename):
|
|
|
|
"""Load FW and zero out signature fiels"""
|
|
|
|
with open(filename, "rb") as f:
|
|
|
|
data = f.read()
|
|
|
|
self.fw_image = bytearray(data)
|
|
|
|
self.zero_sig_fields()
|
|
|
|
|
|
|
|
def zero_sig_fields(self):
|
|
|
|
"""Zero out signature fields to be able to compute header hash"""
|
|
|
|
for i in range(3):
|
|
|
|
sigindex_ofs = self.sigindex_offsets[i]
|
|
|
|
sig_ofs = self.sig_offsets[i]
|
|
|
|
self.fw_image[sigindex_ofs] = 0
|
|
|
|
|
|
|
|
self.fw_image[sig_ofs : sig_ofs + 64] = b"\x00" * 64
|
|
|
|
|
|
|
|
def header_hash_hex(self):
|
|
|
|
return self.header_hash.hex()
|
|
|
|
|
|
|
|
def get_header(self):
|
|
|
|
"""Return header with zeroed out signatures as copy"""
|
|
|
|
return bytes(self.fw_image[:1024])
|
|
|
|
|
|
|
|
def patch_signatures(self):
|
|
|
|
"""
|
|
|
|
Patch signatures from signature_pairs.
|
|
|
|
Requires filling signature_pairs beforehand.
|
|
|
|
"""
|
2022-12-19 20:20:08 +00:00
|
|
|
assert len(self.signature_pairs) <= 3
|
2022-09-20 11:30:36 +00:00
|
|
|
|
2022-12-19 20:20:08 +00:00
|
|
|
for i in range(len(self.signature_pairs)):
|
2022-09-20 11:30:36 +00:00
|
|
|
sigindex_ofs = self.sigindex_offsets[i]
|
|
|
|
sig_ofs = self.sig_offsets[i]
|
|
|
|
(sigindex, sig) = self.signature_pairs[i]
|
|
|
|
|
|
|
|
print(f"Patching sigindex {sigindex} at offset {sigindex_ofs}")
|
|
|
|
assert 1 <= sigindex <= 5
|
|
|
|
self.fw_image[sigindex_ofs] = sigindex
|
|
|
|
|
|
|
|
print(f"Patching signature {sig.hex()} at offset {sig_ofs}")
|
|
|
|
assert len(sig) == 64
|
|
|
|
self.fw_image[sig_ofs : sig_ofs + 64] = sig
|
|
|
|
|
|
|
|
def write_output_fw(self, filename):
|
|
|
|
print(f"Writing output signed FW file {filename}")
|
|
|
|
with open(filename, "wb") as signed_fw_file:
|
|
|
|
signed_fw_file.write(self.fw_image)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
# arg1 - unsigned trezor.bin FW
|
|
|
|
# arg2 - list of 3 signatures and indexes in this format (split by single space):
|
|
|
|
# index_num signature
|
|
|
|
# e.g.
|
|
|
|
# 1 adec956df6282c15ee4344b4cf6edbe435ed4bb13b2b7bebb9920f3d1c4a791a446e492f3ff9b86ca43f28cfce1be97c4eefa65e505e8a936876f01833366d5d
|
|
|
|
|
|
|
|
in_fw_fname = sys.argv[1]
|
|
|
|
signatures_fname = sys.argv[2]
|
|
|
|
|
|
|
|
signatures = Signatures(in_fw_fname)
|
|
|
|
i = 0
|
|
|
|
for line in open(signatures_fname):
|
|
|
|
i += 1
|
|
|
|
print(f"Parsing sig line {i} - {line}")
|
|
|
|
idx, sig = line.rstrip().split(" ")
|
|
|
|
idx = int(idx)
|
|
|
|
sig = bytes.fromhex(sig)
|
|
|
|
assert idx in range(1, 6) # 1 <= idx <= 5
|
|
|
|
assert len(sig) == 64
|
|
|
|
signatures.signature_pairs.append((idx, sig))
|
|
|
|
|
|
|
|
out_fw_name = in_fw_fname + ".signed"
|
|
|
|
signatures.patch_signatures()
|
|
|
|
signatures.write_output_fw(out_fw_name)
|