mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-13 19:18:56 +00:00
bootloader: convert firmware_sign.py to python3
This commit is contained in:
parent
83f1906cad
commit
1d63c7edc2
@ -1,9 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import struct
|
||||
import binascii
|
||||
|
||||
import ecdsa
|
||||
|
||||
try:
|
||||
@ -14,130 +15,161 @@ except:
|
||||
SLOTS = 3
|
||||
|
||||
pubkeys = {
|
||||
1: '04d571b7f148c5e4232c3814f777d8faeaf1a84216c78d569b71041ffc768a5b2d810fc3bb134dd026b57e65005275aedef43e155f48fc11a32ec790a93312bd58',
|
||||
2: '0463279c0c0866e50c05c799d32bd6bab0188b6de06536d1109d2ed9ce76cb335c490e55aee10cc901215132e853097d5432eda06b792073bd7740c94ce4516cb1',
|
||||
3: '0443aedbb6f7e71c563f8ed2ef64ec9981482519e7ef4f4aa98b27854e8c49126d4956d300ab45fdc34cd26bc8710de0a31dbdf6de7435fd0b492be70ac75fde58',
|
||||
4: '04877c39fd7c62237e038235e9c075dab261630f78eeb8edb92487159fffedfdf6046c6f8b881fa407c4a4ce6c28de0b19c1f4e29f1fcbc5a58ffd1432a3e0938a',
|
||||
5: '047384c51ae81add0a523adbb186c91b906ffb64c2c765802bf26dbd13bdf12c319e80c2213a136c8ee03d7874fd22b70d68e7dee469decfbbb510ee9a460cda45',
|
||||
1: "04d571b7f148c5e4232c3814f777d8faeaf1a84216c78d569b71041ffc768a5b2d810fc3bb134dd026b57e65005275aedef43e155f48fc11a32ec790a93312bd58",
|
||||
2: "0463279c0c0866e50c05c799d32bd6bab0188b6de06536d1109d2ed9ce76cb335c490e55aee10cc901215132e853097d5432eda06b792073bd7740c94ce4516cb1",
|
||||
3: "0443aedbb6f7e71c563f8ed2ef64ec9981482519e7ef4f4aa98b27854e8c49126d4956d300ab45fdc34cd26bc8710de0a31dbdf6de7435fd0b492be70ac75fde58",
|
||||
4: "04877c39fd7c62237e038235e9c075dab261630f78eeb8edb92487159fffedfdf6046c6f8b881fa407c4a4ce6c28de0b19c1f4e29f1fcbc5a58ffd1432a3e0938a",
|
||||
5: "047384c51ae81add0a523adbb186c91b906ffb64c2c765802bf26dbd13bdf12c319e80c2213a136c8ee03d7874fd22b70d68e7dee469decfbbb510ee9a460cda45",
|
||||
}
|
||||
|
||||
INDEXES_START = len('TRZR') + struct.calcsize('<I')
|
||||
INDEXES_START = len("TRZR") + struct.calcsize("<I")
|
||||
SIG_START = INDEXES_START + SLOTS + 1 + 52
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description='Commandline tool for signing Trezor firmware.')
|
||||
parser.add_argument('-f', '--file', dest='path', help="Firmware file to modify")
|
||||
parser.add_argument('-s', '--sign', dest='sign', action='store_true', help="Add signature to firmware slot")
|
||||
parser.add_argument('-p', '--pem', dest='pem', action='store_true', help="Use PEM instead of SECEXP")
|
||||
parser.add_argument('-g', '--generate', dest='generate', action='store_true', help='Generate new ECDSA keypair')
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Commandline tool for signing Trezor firmware."
|
||||
)
|
||||
parser.add_argument("-f", "--file", dest="path", help="Firmware file to modify")
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--sign",
|
||||
dest="sign",
|
||||
action="store_true",
|
||||
help="Add signature to firmware slot",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p", "--pem", dest="pem", action="store_true", help="Use PEM instead of SECEXP"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-g",
|
||||
"--generate",
|
||||
dest="generate",
|
||||
action="store_true",
|
||||
help="Generate new ECDSA keypair",
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def prepare(data):
|
||||
# Takes raw OR signed firmware and clean out metadata structure
|
||||
# This produces 'clean' data for signing
|
||||
|
||||
meta = b'TRZR' # magic
|
||||
if data[:4] == b'TRZR':
|
||||
meta += data[4:4 + struct.calcsize('<I')]
|
||||
meta = b"TRZR" # magic
|
||||
if data[:4] == b"TRZR":
|
||||
meta += data[4 : 4 + struct.calcsize("<I")]
|
||||
else:
|
||||
meta += struct.pack('<I', len(data)) # length of the code
|
||||
meta += b'\x00' * SLOTS # signature index #1-#3
|
||||
meta += b'\x01' # flags
|
||||
meta += b'\x00' * 52 # reserved
|
||||
meta += b'\x00' * 64 * SLOTS # signature #1-#3
|
||||
meta += struct.pack("<I", len(data)) # length of the code
|
||||
meta += b"\x00" * SLOTS # signature index #1-#3
|
||||
meta += b"\x01" # flags
|
||||
meta += b"\x00" * 52 # reserved
|
||||
meta += b"\x00" * 64 * SLOTS # signature #1-#3
|
||||
|
||||
if data[:4] == b'TRZR':
|
||||
if data[:4] == b"TRZR":
|
||||
# Replace existing header
|
||||
out = meta + data[len(meta):]
|
||||
out = meta + data[len(meta) :]
|
||||
else:
|
||||
# create data from meta + code
|
||||
out = meta + data
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def check_signatures(data):
|
||||
# Analyses given firmware and prints out
|
||||
# status of included signatures
|
||||
|
||||
try:
|
||||
indexes = [ ord(x) for x in data[INDEXES_START:INDEXES_START + SLOTS] ]
|
||||
indexes = [ord(x) for x in data[INDEXES_START : INDEXES_START + SLOTS]]
|
||||
except:
|
||||
indexes = [ x for x in data[INDEXES_START:INDEXES_START + SLOTS] ]
|
||||
indexes = [x for x in data[INDEXES_START : INDEXES_START + SLOTS]]
|
||||
|
||||
to_sign = prepare(data)[256:] # without meta
|
||||
to_sign = prepare(data)[256:] # without meta
|
||||
fingerprint = hashlib.sha256(to_sign).hexdigest()
|
||||
|
||||
print("Firmware fingerprint:", fingerprint)
|
||||
|
||||
used = []
|
||||
for x in range(SLOTS):
|
||||
signature = data[SIG_START + 64 * x:SIG_START + 64 * x + 64]
|
||||
signature = data[SIG_START + 64 * x : SIG_START + 64 * x + 64]
|
||||
|
||||
if indexes[x] == 0:
|
||||
print("Slot #%d" % (x + 1), 'is empty')
|
||||
print("Slot #%d" % (x + 1), "is empty")
|
||||
else:
|
||||
pk = pubkeys[indexes[x]]
|
||||
verify = ecdsa.VerifyingKey.from_string(binascii.unhexlify(pk)[1:],
|
||||
curve=ecdsa.curves.SECP256k1, hashfunc=hashlib.sha256)
|
||||
verify = ecdsa.VerifyingKey.from_string(
|
||||
bytes.fromhex(pk)[1:],
|
||||
curve=ecdsa.curves.SECP256k1,
|
||||
hashfunc=hashlib.sha256,
|
||||
)
|
||||
|
||||
try:
|
||||
verify.verify(signature, to_sign, hashfunc=hashlib.sha256)
|
||||
|
||||
if indexes[x] in used:
|
||||
print("Slot #%d signature: DUPLICATE" % (x + 1), binascii.hexlify(signature))
|
||||
print("Slot #%d signature: DUPLICATE" % (x + 1), signature.hex())
|
||||
else:
|
||||
used.append(indexes[x])
|
||||
print("Slot #%d signature: VALID" % (x + 1), binascii.hexlify(signature))
|
||||
print("Slot #%d signature: VALID" % (x + 1), signature.hex())
|
||||
|
||||
except:
|
||||
print("Slot #%d signature: INVALID" % (x + 1), binascii.hexlify(signature))
|
||||
print("Slot #%d signature: INVALID" % (x + 1), signature.hex())
|
||||
|
||||
|
||||
def modify(data, slot, index, signature):
|
||||
# Replace signature in data
|
||||
|
||||
# Put index to data
|
||||
data = data[:INDEXES_START + slot - 1 ] + chr(index) + data[INDEXES_START + slot:]
|
||||
data = (
|
||||
data[: INDEXES_START + slot - 1] + bytes([index]) + data[INDEXES_START + slot :]
|
||||
)
|
||||
|
||||
# Put signature to data
|
||||
data = data[:SIG_START + 64 * (slot - 1) ] + signature + data[SIG_START + 64 * slot:]
|
||||
data = (
|
||||
data[: SIG_START + 64 * (slot - 1)] + signature + data[SIG_START + 64 * slot :]
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def sign(data, is_pem):
|
||||
# Ask for index and private key and signs the firmware
|
||||
|
||||
slot = int(raw_input('Enter signature slot (1-%d): ' % SLOTS))
|
||||
slot = int(raw_input("Enter signature slot (1-%d): " % SLOTS))
|
||||
if slot < 1 or slot > SLOTS:
|
||||
raise Exception("Invalid slot")
|
||||
|
||||
if is_pem:
|
||||
print("Paste ECDSA private key in PEM format and press Enter:")
|
||||
print("(blank private key removes the signature on given index)")
|
||||
pem_key = ''
|
||||
pem_key = ""
|
||||
while True:
|
||||
key = raw_input()
|
||||
pem_key += key + "\n"
|
||||
if key == '':
|
||||
if key == "":
|
||||
break
|
||||
if pem_key.strip() == '':
|
||||
if pem_key.strip() == "":
|
||||
# Blank key,let's remove existing signature from slot
|
||||
return modify(data, slot, 0, '\x00' * 64)
|
||||
return modify(data, slot, 0, "\x00" * 64)
|
||||
key = ecdsa.SigningKey.from_pem(pem_key)
|
||||
else:
|
||||
print("Paste SECEXP (in hex) and press Enter:")
|
||||
print("(blank private key removes the signature on given index)")
|
||||
secexp = raw_input()
|
||||
if secexp.strip() == '':
|
||||
if secexp.strip() == "":
|
||||
# Blank key,let's remove existing signature from slot
|
||||
return modify(data, slot, 0, '\x00' * 64)
|
||||
key = ecdsa.SigningKey.from_secret_exponent(secexp = int(secexp, 16), curve=ecdsa.curves.SECP256k1, hashfunc=hashlib.sha256)
|
||||
return modify(data, slot, 0, "\x00" * 64)
|
||||
key = ecdsa.SigningKey.from_secret_exponent(
|
||||
secexp=int(secexp, 16),
|
||||
curve=ecdsa.curves.SECP256k1,
|
||||
hashfunc=hashlib.sha256,
|
||||
)
|
||||
|
||||
to_sign = prepare(data)[256:] # without meta
|
||||
to_sign = prepare(data)[256:] # without meta
|
||||
|
||||
# Locate proper index of current signing key
|
||||
pubkey = b'04' + binascii.hexlify(key.get_verifying_key().to_string())
|
||||
pubkey = "04" + key.get_verifying_key().to_string().hex()
|
||||
index = None
|
||||
for i, pk in pubkeys.items():
|
||||
if pk == pubkey:
|
||||
@ -151,34 +183,35 @@ def sign(data, is_pem):
|
||||
|
||||
return modify(data, slot, index, signature)
|
||||
|
||||
|
||||
def main(args):
|
||||
if args.generate:
|
||||
key = ecdsa.SigningKey.generate(
|
||||
curve=ecdsa.curves.SECP256k1,
|
||||
hashfunc=hashlib.sha256)
|
||||
curve=ecdsa.curves.SECP256k1, hashfunc=hashlib.sha256
|
||||
)
|
||||
|
||||
print("PRIVATE KEY (SECEXP):")
|
||||
print(binascii.hexlify(key.to_string()))
|
||||
print(key.to_string().hex())
|
||||
print()
|
||||
|
||||
print("PRIVATE KEY (PEM):")
|
||||
print(key.to_pem())
|
||||
|
||||
print("PUBLIC KEY:")
|
||||
print('04' + binascii.hexlify(key.get_verifying_key().to_string()))
|
||||
print("04" + key.get_verifying_key().to_string().hex())
|
||||
return
|
||||
|
||||
if not args.path:
|
||||
raise Exception("-f/--file is required")
|
||||
|
||||
data = open(args.path, 'rb').read()
|
||||
data = open(args.path, "rb").read()
|
||||
assert len(data) % 4 == 0
|
||||
|
||||
if data[:4] != b'TRZR':
|
||||
if data[:4] != b"TRZR":
|
||||
print("Metadata has been added...")
|
||||
data = prepare(data)
|
||||
|
||||
if data[:4] != b'TRZR':
|
||||
if data[:4] != b"TRZR":
|
||||
raise Exception("Firmware header expected")
|
||||
|
||||
print("Firmware size %d bytes" % len(data))
|
||||
@ -189,10 +222,11 @@ def main(args):
|
||||
data = sign(data, args.pem)
|
||||
check_signatures(data)
|
||||
|
||||
fp = open(args.path, 'wb')
|
||||
fp = open(args.path, "wb")
|
||||
fp.write(data)
|
||||
fp.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_args()
|
||||
main(args)
|
||||
|
Loading…
Reference in New Issue
Block a user