mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-05-05 08:29:13 +00:00
feat(legacy): qa build for upgrade testing
This commit is contained in:
parent
9d64039245
commit
3cd47f85af
@ -21,9 +21,11 @@ CROSS_PORT_OPTS ?=
|
|||||||
PRODUCTION ?= 0
|
PRODUCTION ?= 0
|
||||||
PYOPT ?= 1
|
PYOPT ?= 1
|
||||||
BITCOIN_ONLY ?= 0
|
BITCOIN_ONLY ?= 0
|
||||||
|
BOOTLOADER_QA ?= 0
|
||||||
TREZOR_MODEL ?= T
|
TREZOR_MODEL ?= T
|
||||||
TREZOR_MEMPERF ?= 0
|
TREZOR_MEMPERF ?= 0
|
||||||
ADDRESS_SANITIZER ?= 0
|
ADDRESS_SANITIZER ?= 0
|
||||||
|
UI2 ?= 0
|
||||||
|
|
||||||
# OpenOCD interface default. Alternative: ftdi/olimex-arm-usb-tiny-h
|
# OpenOCD interface default. Alternative: ftdi/olimex-arm-usb-tiny-h
|
||||||
OPENOCD_INTERFACE ?= stlink
|
OPENOCD_INTERFACE ?= stlink
|
||||||
@ -164,7 +166,7 @@ build_reflash: ## build reflash firmware + reflash image
|
|||||||
dd if=build/bootloader/bootloader.bin of=$(REFLASH_BUILD_DIR)/sdimage.bin bs=1 seek=49152
|
dd if=build/bootloader/bootloader.bin of=$(REFLASH_BUILD_DIR)/sdimage.bin bs=1 seek=49152
|
||||||
|
|
||||||
build_firmware: templates build_cross ## build firmware with frozen modules
|
build_firmware: templates build_cross ## build firmware with frozen modules
|
||||||
$(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" $(FIRMWARE_BUILD_DIR)/firmware.bin
|
$(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" BOOTLOADER_QA="$(BOOTLOADER_QA)" $(FIRMWARE_BUILD_DIR)/firmware.bin
|
||||||
|
|
||||||
build_unix: templates ## build unix port
|
build_unix: templates ## build unix port
|
||||||
$(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" PYOPT="0" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)"
|
$(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" PYOPT="0" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)"
|
||||||
|
@ -5,10 +5,15 @@ import os
|
|||||||
import tools
|
import tools
|
||||||
|
|
||||||
BITCOIN_ONLY = ARGUMENTS.get('BITCOIN_ONLY', '0')
|
BITCOIN_ONLY = ARGUMENTS.get('BITCOIN_ONLY', '0')
|
||||||
|
PRODUCTION = ARGUMENTS.get('PRODUCTION', '0')
|
||||||
|
BOOTLOADER_QA = ARGUMENTS.get('BOOTLOADER_QA', '0') == '1'
|
||||||
EVERYTHING = BITCOIN_ONLY != '1'
|
EVERYTHING = BITCOIN_ONLY != '1'
|
||||||
TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T')
|
TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T')
|
||||||
DMA2D = TREZOR_MODEL in ('T', )
|
DMA2D = TREZOR_MODEL in ('T', )
|
||||||
|
|
||||||
|
if PRODUCTION != '1' and BOOTLOADER_QA:
|
||||||
|
raise ValueError('Firmware variant for bootloader upgrade testing must be done with PRODUCTION=1')
|
||||||
|
|
||||||
FEATURE_FLAGS = {
|
FEATURE_FLAGS = {
|
||||||
"RDI": True,
|
"RDI": True,
|
||||||
"SECP256K1_ZKP": True, # required for trezor.crypto.curve.bip340 (BIP340/Taproot)
|
"SECP256K1_ZKP": True, # required for trezor.crypto.curve.bip340 (BIP340/Taproot)
|
||||||
@ -431,7 +436,7 @@ tools.add_font('MONO', FONT_MONO, CPPDEFINES_MOD, SOURCE_MOD)
|
|||||||
|
|
||||||
SOURCE_QSTR = SOURCE_MOD + SOURCE_MICROPYTHON + SOURCE_MICROPYTHON_SPEED
|
SOURCE_QSTR = SOURCE_MOD + SOURCE_MICROPYTHON + SOURCE_MICROPYTHON_SPEED
|
||||||
|
|
||||||
env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s -DPYOPT=%s -DBITCOIN_ONLY=%s' % (ARGUMENTS.get('CFLAGS', ''), ARGUMENTS.get('PRODUCTION', '0'), PYOPT, BITCOIN_ONLY))
|
env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s -DPYOPT=%s -DBITCOIN_ONLY=%s' % (ARGUMENTS.get('CFLAGS', ''), PRODUCTION, PYOPT, BITCOIN_ONLY))
|
||||||
|
|
||||||
tools.configure_board(TREZOR_MODEL, env, CPPDEFINES_MOD, SOURCE_TREZORHAL)
|
tools.configure_board(TREZOR_MODEL, env, CPPDEFINES_MOD, SOURCE_TREZORHAL)
|
||||||
|
|
||||||
@ -769,12 +774,18 @@ obj_program.extend(
|
|||||||
' --rename-section .data=.vendorheader,alloc,load,readonly,contents'
|
' --rename-section .data=.vendorheader,alloc,load,readonly,contents'
|
||||||
' $SOURCE $TARGET', ))
|
' $SOURCE $TARGET', ))
|
||||||
|
|
||||||
|
|
||||||
|
BOOTLOADER_SUFFIX = TREZOR_MODEL + ('_QA' if BOOTLOADER_QA else '')
|
||||||
|
|
||||||
obj_program.extend(
|
obj_program.extend(
|
||||||
env.Command(
|
env.Command(
|
||||||
target='embed/firmware/bootloader.o',
|
target='embed/firmware/bootloaders/bootloader.o',
|
||||||
source='embed/firmware/bootloader.bin',
|
source=f'embed/firmware/bootloaders/bootloader_{BOOTLOADER_SUFFIX}.bin',
|
||||||
action='$OBJCOPY -I binary -O elf32-littlearm -B arm'
|
action='$OBJCOPY -I binary -O elf32-littlearm -B arm'
|
||||||
' --rename-section .data=.bootloader'
|
' --rename-section .data=.bootloader'
|
||||||
|
f' --redefine-sym _binary_embed_firmware_bootloaders_bootloader_{BOOTLOADER_SUFFIX}_bin_start=_binary_embed_firmware_bootloader_bin_start'
|
||||||
|
f' --redefine-sym _binary_embed_firmware_bootloaders_bootloader_{BOOTLOADER_SUFFIX}_bin_end=_binary_embed_firmware_bootloader_bin_end'
|
||||||
|
f' --redefine-sym _binary_embed_firmware_bootloaders_bootloader_{BOOTLOADER_SUFFIX}_bin_size=_binary_embed_firmware_bootloader_bin_size'
|
||||||
' $SOURCE $TARGET', ))
|
' $SOURCE $TARGET', ))
|
||||||
|
|
||||||
env.Depends(obj_program, qstr_generated)
|
env.Depends(obj_program, qstr_generated)
|
||||||
|
1
core/embed/firmware/bootloaders/bootloader_1.bin
Symbolic link
1
core/embed/firmware/bootloaders/bootloader_1.bin
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../../../legacy/firmware/bootloader.dat
|
@ -156,6 +156,18 @@ CPUFLAGS += -DPRODUCTION=1
|
|||||||
$(info PRODUCTION=1)
|
$(info PRODUCTION=1)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
BOOTLOADER_QA ?= 0
|
||||||
|
ifeq ($(BOOTLOADER_QA), 0)
|
||||||
|
CFLAGS += -DBOOTLOADER_QA=0
|
||||||
|
CPUFLAGS += -DBOOTLOADER_QA=0
|
||||||
|
$(info BOOTLOADER_QA=0)
|
||||||
|
else
|
||||||
|
CFLAGS += -DBOOTLOADER_QA=1
|
||||||
|
CPUFLAGS += -DBOOTLOADER_QA=1
|
||||||
|
$(info BOOTLOADER_QA=1)
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
ifeq ($(DEBUG_RNG), 1)
|
ifeq ($(DEBUG_RNG), 1)
|
||||||
CFLAGS += -DDEBUG_RNG=1
|
CFLAGS += -DDEBUG_RNG=1
|
||||||
else
|
else
|
||||||
|
@ -64,6 +64,7 @@ void show_unplug(const char *line1, const char *line2) {
|
|||||||
"You may now", "unplug your Trezor.", NULL);
|
"You may now", "unplug your Trezor.", NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !BOOTLOADER_QA
|
||||||
static void show_unofficial_warning(const uint8_t *hash) {
|
static void show_unofficial_warning(const uint8_t *hash) {
|
||||||
// On production bootloader, show warning and wait for user
|
// On production bootloader, show warning and wait for user
|
||||||
// to accept or reject it
|
// to accept or reject it
|
||||||
@ -93,6 +94,7 @@ static void show_unofficial_warning(const uint8_t *hash) {
|
|||||||
delay(100000000);
|
delay(100000000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void __attribute__((noreturn)) load_app(int signed_firmware) {
|
static void __attribute__((noreturn)) load_app(int signed_firmware) {
|
||||||
// zero out SRAM
|
// zero out SRAM
|
||||||
@ -159,12 +161,16 @@ int main(void) {
|
|||||||
uint8_t fingerprint[32] = {0};
|
uint8_t fingerprint[32] = {0};
|
||||||
int signed_firmware = signatures_match(hdr, fingerprint);
|
int signed_firmware = signatures_match(hdr, fingerprint);
|
||||||
if (SIG_OK != signed_firmware) {
|
if (SIG_OK != signed_firmware) {
|
||||||
|
#if BOOTLOADER_QA
|
||||||
|
show_halt("Unsigned firmware", "Won't run on QA device");
|
||||||
|
#else
|
||||||
show_unofficial_warning(fingerprint);
|
show_unofficial_warning(fingerprint);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#if !PRODUCTION
|
#if !PRODUCTION && !BOOTLOADER_QA && !DEBUG_T1_SIGNATURES
|
||||||
// try to avoid bricking board SWD debug by accident
|
// try to avoid bricking board SWD debug by accident
|
||||||
else {
|
else {
|
||||||
show_halt("Official firmware", "Won't flash on debug device");
|
show_halt("Official firmware", "Won't run on debug device");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
333
legacy/bootloader/firmware_sign_dev.py
Normal file
333
legacy/bootloader/firmware_sign_dev.py
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import struct
|
||||||
|
|
||||||
|
import ecdsa
|
||||||
|
from ecdsa import BadSignatureError
|
||||||
|
|
||||||
|
SLOTS = 3
|
||||||
|
|
||||||
|
pubkeys_dev = {
|
||||||
|
1: "042c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991ae31a9c671a36543f46cea8fce6984608aa316aa0472a7eed08847440218cb2f",
|
||||||
|
2: "04edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f2211452c88a66eb8ac3c19a1cc3a3fc6d72506f6fce2025f738d8b55f29f22125eb0a4",
|
||||||
|
3: "04665f660a5052be7a95546a02179058d93d3e08a779734914594346075bb0afd45113948d72cf3dc8f2b70ee02dc1695d051bb0c6da2a914a69045e3277682d3b",
|
||||||
|
}
|
||||||
|
|
||||||
|
privkeys_dev = {
|
||||||
|
1: "0x4444444444444444444444444444444444444444444444444444444444444444",
|
||||||
|
2: "0x4545454545454545454545454545454545454545454545454545454545454545",
|
||||||
|
3: "0xbfc4bca9c9c228a16639d3503d999a733a439210b64cebe757a4fd03ca46a5c8",
|
||||||
|
}
|
||||||
|
|
||||||
|
FWHEADER_SIZE = 1024
|
||||||
|
SIGNATURES_START = 6 * 4 + 8 + 512
|
||||||
|
INDEXES_START = SIGNATURES_START + 3 * 64
|
||||||
|
|
||||||
|
INDEXES_START_OLD = len("TRZR") + struct.calcsize("<I")
|
||||||
|
SIG_START = INDEXES_START_OLD + 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")
|
||||||
|
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def pad_to_size(data, size):
|
||||||
|
if len(data) > size:
|
||||||
|
raise ValueError("Chunk too big already")
|
||||||
|
if len(data) == size:
|
||||||
|
return data
|
||||||
|
return data + b"\xFF" * (size - len(data))
|
||||||
|
|
||||||
|
|
||||||
|
# see memory.h for details
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_hashes(data):
|
||||||
|
# process chunks
|
||||||
|
start = 0
|
||||||
|
end = (64 - 1) * 1024
|
||||||
|
hashes = []
|
||||||
|
for i in range(16):
|
||||||
|
sector = data[start:end]
|
||||||
|
if len(sector) > 0:
|
||||||
|
chunk = pad_to_size(sector, end - start)
|
||||||
|
hashes.append(hashlib.sha256(chunk).digest())
|
||||||
|
else:
|
||||||
|
hashes.append(b"\x00" * 32)
|
||||||
|
start = end
|
||||||
|
end += 64 * 1024
|
||||||
|
return hashes
|
||||||
|
|
||||||
|
|
||||||
|
def check_hashes(data):
|
||||||
|
expected_hashes = data[0x20 : 0x20 + 16 * 32]
|
||||||
|
hashes = b""
|
||||||
|
for h in prepare_hashes(data[FWHEADER_SIZE:]):
|
||||||
|
hashes += h
|
||||||
|
|
||||||
|
if expected_hashes == hashes:
|
||||||
|
print("HASHES OK")
|
||||||
|
else:
|
||||||
|
print("HASHES NOT OK")
|
||||||
|
|
||||||
|
|
||||||
|
def update_hashes_in_header(data):
|
||||||
|
# Store hashes in the firmware header
|
||||||
|
data = bytearray(data)
|
||||||
|
o = 0
|
||||||
|
for h in prepare_hashes(data[FWHEADER_SIZE:]):
|
||||||
|
data[0x20 + o : 0x20 + o + 32] = h
|
||||||
|
o += 32
|
||||||
|
return bytes(data)
|
||||||
|
|
||||||
|
|
||||||
|
def get_header(data, zero_signatures=False):
|
||||||
|
if not zero_signatures:
|
||||||
|
return data[:FWHEADER_SIZE]
|
||||||
|
else:
|
||||||
|
data = bytearray(data[:FWHEADER_SIZE])
|
||||||
|
data[SIGNATURES_START : SIGNATURES_START + 3 * 64 + 3] = b"\x00" * (3 * 64 + 3)
|
||||||
|
return bytes(data)
|
||||||
|
|
||||||
|
|
||||||
|
def check_size(data):
|
||||||
|
size = struct.unpack("<L", data[12:16])[0]
|
||||||
|
assert size == len(data) - 1024
|
||||||
|
|
||||||
|
|
||||||
|
def check_signatures(data):
|
||||||
|
# Analyses given firmware and prints out
|
||||||
|
# status of included signatures
|
||||||
|
|
||||||
|
indexes = [x for x in data[INDEXES_START : INDEXES_START + SLOTS]]
|
||||||
|
|
||||||
|
to_sign = get_header(data, zero_signatures=True)
|
||||||
|
fingerprint = hashlib.sha256(to_sign).hexdigest()
|
||||||
|
|
||||||
|
print("Firmware fingerprint:", fingerprint)
|
||||||
|
|
||||||
|
used = []
|
||||||
|
for x in range(SLOTS):
|
||||||
|
signature = data[SIGNATURES_START + 64 * x : SIGNATURES_START + 64 * x + 64]
|
||||||
|
|
||||||
|
if indexes[x] == 0:
|
||||||
|
print(f"Slot #{x + 1}", "is empty")
|
||||||
|
else:
|
||||||
|
pubkeys = pubkeys_dev
|
||||||
|
pk = pubkeys[indexes[x]]
|
||||||
|
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(f"Slot #{x + 1} signature: DUPLICATE", signature.hex())
|
||||||
|
else:
|
||||||
|
used.append(indexes[x])
|
||||||
|
print(f"Slot #{x + 1} signature: VALID", signature.hex())
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
print(f"Slot #{x + 1} signature: INVALID", signature.hex())
|
||||||
|
|
||||||
|
|
||||||
|
def modify(data, slot, index, signature):
|
||||||
|
data = bytearray(data)
|
||||||
|
# put index to data
|
||||||
|
data[INDEXES_START + slot - 1] = index
|
||||||
|
# put signature to data
|
||||||
|
data[SIGNATURES_START + 64 * (slot - 1) : SIGNATURES_START + 64 * slot] = signature
|
||||||
|
return bytes(data)
|
||||||
|
|
||||||
|
|
||||||
|
def sign(data, slot, secexp):
|
||||||
|
key = ecdsa.SigningKey.from_secret_exponent(
|
||||||
|
secexp=int(secexp, 16),
|
||||||
|
curve=ecdsa.curves.SECP256k1,
|
||||||
|
hashfunc=hashlib.sha256,
|
||||||
|
)
|
||||||
|
|
||||||
|
to_sign = get_header(data, zero_signatures=True)
|
||||||
|
|
||||||
|
# Locate proper index of current signing key
|
||||||
|
pubkey = "04" + key.get_verifying_key().to_string().hex()
|
||||||
|
index = None
|
||||||
|
|
||||||
|
pubkeys = pubkeys_dev
|
||||||
|
for i, pk in pubkeys.items():
|
||||||
|
if pk == pubkey:
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
|
||||||
|
if index is None:
|
||||||
|
raise Exception("Unable to find private key index. Unknown private key?")
|
||||||
|
|
||||||
|
signature = key.sign_deterministic(to_sign, hashfunc=hashlib.sha256)
|
||||||
|
|
||||||
|
return modify(data, slot, index, signature)
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_old(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")]
|
||||||
|
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
|
||||||
|
|
||||||
|
if data[:4] == b"TRZR":
|
||||||
|
# Replace existing header
|
||||||
|
out = meta + data[len(meta) :]
|
||||||
|
else:
|
||||||
|
# create data from meta + code
|
||||||
|
out = meta + data
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def check_signatures_old(data):
|
||||||
|
# Analyses given firmware and prints out
|
||||||
|
# status of included signatures
|
||||||
|
|
||||||
|
try:
|
||||||
|
indexes = [ord(x) for x in data[INDEXES_START_OLD : INDEXES_START_OLD + SLOTS]]
|
||||||
|
except TypeError:
|
||||||
|
indexes = [x for x in data[INDEXES_START_OLD : INDEXES_START_OLD + SLOTS]]
|
||||||
|
|
||||||
|
to_sign = prepare_old(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]
|
||||||
|
|
||||||
|
if indexes[x] == 0:
|
||||||
|
print("Slot #%d" % (x + 1), "is empty")
|
||||||
|
else:
|
||||||
|
pubkeys = pubkeys_dev
|
||||||
|
pk = pubkeys[indexes[x]]
|
||||||
|
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), signature.hex())
|
||||||
|
else:
|
||||||
|
used.append(indexes[x])
|
||||||
|
print("Slot #%d signature: VALID" % (x + 1), signature.hex())
|
||||||
|
|
||||||
|
except BadSignatureError:
|
||||||
|
print("Slot #%d signature: INVALID" % (x + 1), signature.hex())
|
||||||
|
|
||||||
|
|
||||||
|
def modify_old(data, slot, index, signature):
|
||||||
|
# Replace signature in data
|
||||||
|
|
||||||
|
# Put index to data
|
||||||
|
data = (
|
||||||
|
data[: INDEXES_START_OLD + slot - 1]
|
||||||
|
+ bytes([index])
|
||||||
|
+ data[INDEXES_START_OLD + slot :]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Put signature to data
|
||||||
|
data = (
|
||||||
|
data[: SIG_START + 64 * (slot - 1)] + signature + data[SIG_START + 64 * slot :]
|
||||||
|
)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def sign_old(data, slot, secexp):
|
||||||
|
key = ecdsa.SigningKey.from_secret_exponent(
|
||||||
|
secexp=int(secexp, 16),
|
||||||
|
curve=ecdsa.curves.SECP256k1,
|
||||||
|
hashfunc=hashlib.sha256,
|
||||||
|
)
|
||||||
|
|
||||||
|
to_sign = prepare_old(data)[256:] # without meta
|
||||||
|
|
||||||
|
# Locate proper index of current signing key
|
||||||
|
pubkey = "04" + key.get_verifying_key().to_string().hex()
|
||||||
|
index = None
|
||||||
|
|
||||||
|
pubkeys = pubkeys_dev
|
||||||
|
|
||||||
|
for i, pk in pubkeys.items():
|
||||||
|
if pk == pubkey:
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
|
||||||
|
if index is None:
|
||||||
|
raise Exception("Unable to find private key index. Unknown private key?")
|
||||||
|
|
||||||
|
signature = key.sign_deterministic(to_sign, hashfunc=hashlib.sha256)
|
||||||
|
|
||||||
|
return modify_old(data, slot, index, signature)
|
||||||
|
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
if not args.path:
|
||||||
|
raise Exception("-f/--file is required")
|
||||||
|
|
||||||
|
data = open(args.path, "rb").read()
|
||||||
|
assert len(data) % 4 == 0
|
||||||
|
|
||||||
|
if data[:4] != b"TRZF":
|
||||||
|
raise Exception("Firmware header expected")
|
||||||
|
|
||||||
|
data = update_hashes_in_header(data)
|
||||||
|
|
||||||
|
print(f"Firmware size {len(data)} bytes")
|
||||||
|
|
||||||
|
check_size(data)
|
||||||
|
check_signatures(data)
|
||||||
|
check_hashes(data)
|
||||||
|
|
||||||
|
data = sign(data, 1, privkeys_dev[1])
|
||||||
|
data = sign(data, 2, privkeys_dev[2])
|
||||||
|
data = sign(data, 3, privkeys_dev[3])
|
||||||
|
check_signatures(data)
|
||||||
|
check_hashes(data)
|
||||||
|
|
||||||
|
fp = open(args.path, "wb")
|
||||||
|
fp.write(data)
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
data = prepare_old(data)
|
||||||
|
check_signatures_old(data)
|
||||||
|
data = sign_old(data, 1, privkeys_dev[1])
|
||||||
|
data = sign_old(data, 2, privkeys_dev[2])
|
||||||
|
data = sign_old(data, 3, privkeys_dev[3])
|
||||||
|
check_signatures_old(data)
|
||||||
|
|
||||||
|
fp = open(args.path, "wb")
|
||||||
|
fp.write(data)
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = parse_args()
|
||||||
|
main(args)
|
@ -173,9 +173,15 @@ endif
|
|||||||
@printf " MAKO $@\n"
|
@printf " MAKO $@\n"
|
||||||
$(Q)$(PYTHON) ../vendor/trezor-common/tools/cointool.py render $(MAKO_RENDER_FLAG) $@.mako
|
$(Q)$(PYTHON) ../vendor/trezor-common/tools/cointool.py render $(MAKO_RENDER_FLAG) $@.mako
|
||||||
|
|
||||||
|
ifeq ($(BOOTLOADER_QA), 0)
|
||||||
bl_data.h: bl_data.py bootloader.dat
|
bl_data.h: bl_data.py bootloader.dat
|
||||||
@printf " PYTHON bl_data.py\n"
|
@printf " PYTHON bl_data.py bootloader.dat\n"
|
||||||
$(Q)$(PYTHON) bl_data.py
|
$(Q)$(PYTHON) bl_data.py bootloader.dat
|
||||||
|
else
|
||||||
|
bl_data.h: bl_data.py bootloader_qa.dat
|
||||||
|
@printf " PYTHON bl_data.py bootloader_qa.dat\n"
|
||||||
|
$(Q)$(PYTHON) bl_data.py bootloader_qa.dat
|
||||||
|
endif
|
||||||
|
|
||||||
header.o: version.h
|
header.o: version.h
|
||||||
|
|
||||||
|
@ -26,6 +26,81 @@
|
|||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
#if BOOTLOADER_QA
|
||||||
|
static int known_bootloader(int r, const uint8_t *hash) {
|
||||||
|
if (r != 32) return 0;
|
||||||
|
if (0 ==
|
||||||
|
memcmp(hash,
|
||||||
|
"\x1b\xd8\x1c\x0a\x82\x20\x43\x28\xe9\xaf\x7b\xb6\xb1\x6b\x45\x27"
|
||||||
|
"\x81\x31\xcc\x76\x0a\xfa\x9f\xd0\x81\x6e\xc1\xe1\x12\x86\x49\x83",
|
||||||
|
32))
|
||||||
|
return 1; // 1.3.2
|
||||||
|
if (0 ==
|
||||||
|
memcmp(hash,
|
||||||
|
"\x19\x27\x59\xce\x15\x51\xc4\x21\x03\xcc\x9b\xe8\x4e\x06\x54\x24"
|
||||||
|
"\x68\x8a\x06\xfa\xbb\xe6\xb0\x19\x18\x11\xcd\xbc\x19\x6c\x2f\x1b",
|
||||||
|
32))
|
||||||
|
return 1; // 1.3.3
|
||||||
|
if (0 ==
|
||||||
|
memcmp(hash,
|
||||||
|
"\x09\x34\x2f\x51\x56\x58\xc1\xe5\x95\x64\x1c\x4f\x8d\xf1\xce\x2b"
|
||||||
|
"\x2b\x6a\x86\x39\x14\x35\x0a\x97\x0d\x6e\x4f\x37\xb1\xd7\xde\x65",
|
||||||
|
32))
|
||||||
|
return 1; // 1.4.0
|
||||||
|
if (0 ==
|
||||||
|
memcmp(hash,
|
||||||
|
"\x73\x37\xfa\x82\x2b\x62\x90\x50\x49\x3c\x6c\x39\x3a\x9f\x17\x41"
|
||||||
|
"\x75\xe8\x1b\x39\xf3\xb7\x1b\xff\xab\xa2\xb9\x79\x7f\xf1\x0a\x2b",
|
||||||
|
32))
|
||||||
|
return 1; // 1.5.0
|
||||||
|
if (0 ==
|
||||||
|
memcmp(hash,
|
||||||
|
"\x5c\x8b\x5e\xfc\x8d\x8f\x78\xf3\xa0\x23\x9d\x5f\x90\x95\x4d\xe7"
|
||||||
|
"\xe6\xb5\xb7\x09\xc9\x1c\x3a\xb2\xaa\x14\x6f\x0e\x26\x6e\x70\x60",
|
||||||
|
32))
|
||||||
|
return 1; // 1.5.1
|
||||||
|
if (0 ==
|
||||||
|
memcmp(hash,
|
||||||
|
"\xec\xbd\x06\xd3\x37\x25\xd5\xa6\xbe\xba\x2b\xe3\xde\xf4\x64\x7e"
|
||||||
|
"\xef\x5d\x7b\xb9\x2b\x0c\x19\x8d\xd2\x89\x47\x3b\xad\xd3\x4c\x97",
|
||||||
|
32))
|
||||||
|
return 1; // 1.6.0
|
||||||
|
if (0 ==
|
||||||
|
memcmp(hash,
|
||||||
|
"\xec\xca\xff\x24\x34\xdf\x3b\x49\xef\x00\x0c\x0f\x07\x1f\xa5\x60"
|
||||||
|
"\x18\x16\xfa\x19\x56\x0b\x23\xb4\x73\x52\x0e\x68\xc5\x2d\xe5\x7a",
|
||||||
|
32))
|
||||||
|
return 1; // 1.6.1
|
||||||
|
if (0 ==
|
||||||
|
memcmp(hash,
|
||||||
|
"\xe0\xc7\xfd\xb9\x1a\x14\xcb\x39\xd7\xc1\x43\xff\x70\xd2\x3a\x0b"
|
||||||
|
"\xfb\x0a\xef\xb5\x49\xb6\x15\x0a\xa9\x09\xf8\x35\x0a\xa5\xeb\xa2",
|
||||||
|
32))
|
||||||
|
return 1; // 1.8.0
|
||||||
|
if (0 ==
|
||||||
|
memcmp(hash,
|
||||||
|
"\xf9\x9f\x49\xf8\xd0\xc3\xfa\x82\xd6\xad\x1e\xf4\xf2\xf2\xd7\x2d"
|
||||||
|
"\x01\xa5\xdc\xa3\x6f\x11\xba\xb9\x13\xbd\xfe\xaf\x84\xb2\x6f\x4a",
|
||||||
|
32))
|
||||||
|
return 1; // 1.10.0
|
||||||
|
if (0 ==
|
||||||
|
memcmp(hash,
|
||||||
|
"\x8f\x83\x57\x70\x11\x75\xaf\x5c\x1a\xe5\xb9\x6f\x13\x42\x2f\x7f"
|
||||||
|
"\x1a\x52\xed\x96\xcd\xa0\x18\x07\x63\x0e\x0d\x25\x6c\xd9\x18\x78",
|
||||||
|
32))
|
||||||
|
return 1; // 1.11.0
|
||||||
|
// BEGIN AUTO-GENERATED QA BOOTLOADER ENTRIES (bl_check_qa.txt)
|
||||||
|
if (0 ==
|
||||||
|
memcmp(hash,
|
||||||
|
"\x29\x34\xf3\xd3\xaa\x59\x2b\x34\x49\x6a\x23\xb8\x14\x74\xe2\xc2"
|
||||||
|
"\xf3\xee\x1e\x36\x66\x0b\xf0\x1e\x19\x8a\x65\x15\xc2\x32\xc1\x85",
|
||||||
|
32))
|
||||||
|
return 1; // 1.12.0 shipped with fw 1.12.0
|
||||||
|
// END AUTO-GENERATED QA BOOTLOADER ENTRIES (bl_check_qa.txt)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if PRODUCTION
|
#if PRODUCTION
|
||||||
|
|
||||||
static int known_bootloader(int r, const uint8_t *hash) {
|
static int known_bootloader(int r, const uint8_t *hash) {
|
||||||
@ -138,6 +213,8 @@ static int known_bootloader(int r, const uint8_t *hash) {
|
|||||||
"\xe4\xc3\x21\x9c\x9f\x1b\xb3\xa5\x77\x2f\xfd\x60\x9a\xf9\xe8\xe2",
|
"\xe4\xc3\x21\x9c\x9f\x1b\xb3\xa5\x77\x2f\xfd\x60\x9a\xf9\xe8\xe2",
|
||||||
32))
|
32))
|
||||||
return 1; // 1.11.0 shipped with fw 1.11.1
|
return 1; // 1.11.0 shipped with fw 1.11.1
|
||||||
|
// BEGIN AUTO-GENERATED BOOTLOADER ENTRIES (bl_check.txt)
|
||||||
|
// END AUTO-GENERATED BOOTLOADER ENTRIES (bl_check.txt)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -149,7 +226,7 @@ static int known_bootloader(int r, const uint8_t *hash) {
|
|||||||
* @param shutdown_on_replace: if true, shuts down device instead of return
|
* @param shutdown_on_replace: if true, shuts down device instead of return
|
||||||
*/
|
*/
|
||||||
void check_and_replace_bootloader(bool shutdown_on_replace) {
|
void check_and_replace_bootloader(bool shutdown_on_replace) {
|
||||||
#if PRODUCTION
|
#if PRODUCTION || BOOTLOADER_QA
|
||||||
uint8_t hash[32] = {0};
|
uint8_t hash[32] = {0};
|
||||||
int r = memory_bootloader_hash(hash);
|
int r = memory_bootloader_hash(hash);
|
||||||
|
|
||||||
|
1
legacy/firmware/bl_check_qa.txt
Normal file
1
legacy/firmware/bl_check_qa.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
2934f3d3aa592b34496a23b81474e2c2f3ee1e36660bf01e198a6515c232c185 1.12.0 shipped with fw 1.12.0
|
@ -1,7 +1,10 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
import sys
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
|
||||||
fn = "bootloader.dat"
|
fn = sys.argv[1]
|
||||||
|
|
||||||
|
print("Embedding bootloader", fn)
|
||||||
|
|
||||||
data = open(fn, "rb").read()
|
data = open(fn, "rb").read()
|
||||||
if len(data) > 32768:
|
if len(data) > 32768:
|
||||||
@ -18,9 +21,10 @@ with open("bl_data.h", "wt") as f:
|
|||||||
f.write(f"static const uint8_t bl_hash[32] = {{{bl_hash}}};\n")
|
f.write(f"static const uint8_t bl_hash[32] = {{{bl_hash}}};\n")
|
||||||
f.write(f"static const uint8_t bl_data[32768] = {{{bl_data}}};\n")
|
f.write(f"static const uint8_t bl_data[32768] = {{{bl_data}}};\n")
|
||||||
|
|
||||||
# make sure the last item listed in known_bootloader function
|
if fn != "bootloader_qa.dat":
|
||||||
# is our bootloader
|
# make sure the last item listed in known_bootloader function
|
||||||
with open("bl_check.c", "rt") as f:
|
# is our bootloader
|
||||||
|
with open("bl_check.c", "rt") as f:
|
||||||
hashes = []
|
hashes = []
|
||||||
for l in f.readlines():
|
for l in f.readlines():
|
||||||
if not len(l) >= 78 or not l.startswith(' "\\x'):
|
if not len(l) >= 78 or not l.startswith(' "\\x'):
|
||||||
|
BIN
legacy/firmware/bootloader_qa.dat
Normal file
BIN
legacy/firmware/bootloader_qa.dat
Normal file
Binary file not shown.
@ -64,6 +64,30 @@ static const uint8_t * const pubkey_v3[PUBKEYS] = {
|
|||||||
|
|
||||||
// the "new", or second signing scheme keys
|
// the "new", or second signing scheme keys
|
||||||
|
|
||||||
|
#if BOOTLOADER_QA
|
||||||
|
/*
|
||||||
|
Previously used for QA bootloader upgrade tests
|
||||||
|
|
||||||
|
Debug private keys for v2 (previously called "new") scheme
|
||||||
|
corresponding to pubkeys below as python hexstring array:
|
||||||
|
|
||||||
|
|
||||||
|
['4444444444444444444444444444444444444444444444444444444444444444',
|
||||||
|
'4545454545454545454545454545454545454545454545454545454545454545',
|
||||||
|
'bfc4bca9c9c228a16639d3503d999a733a439210b64cebe757a4fd03ca46a5c8',
|
||||||
|
'5518381d95e93e8eb68a294354989906e3828f36b4556a2ad85d8333294eb1b7',
|
||||||
|
'1d1d34168760dec092c9ff89377d8659076d2dfd95e0281719c15f90d067e211']
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const uint8_t * const pubkey_v2[PUBKEYS] = {
|
||||||
|
(const uint8_t *)"\x03\x2c\x0b\x7c\xf9\x53\x24\xa0\x7d\x05\x39\x8b\x24\x01\x74\xdc\x0c\x2b\xe4\x44\xd9\x6b\x15\x9a\xa6\xc7\xf7\xb1\xe6\x68\x68\x09\x91",
|
||||||
|
(const uint8_t *)"\x02\xed\xab\xbd\x16\xb4\x1c\x83\x71\xb9\x2e\xf2\xf0\x4c\x11\x85\xb4\xf0\x3b\x6d\xcd\x52\xba\x9b\x78\xd9\xd7\xc8\x9c\x8f\x22\x11\x45",
|
||||||
|
(const uint8_t *)"\x03\x66\x5f\x66\x0a\x50\x52\xbe\x7a\x95\x54\x6a\x02\x17\x90\x58\xd9\x3d\x3e\x08\xa7\x79\x73\x49\x14\x59\x43\x46\x07\x5b\xb0\xaf\xd4",
|
||||||
|
(const uint8_t *)"\x03\x66\x63\x5d\x99\x94\x17\xb6\x55\x66\x86\x6c\x65\x63\x0d\x97\x7a\x7a\xe7\x23\xfe\x5f\x6c\x4c\xd1\x7f\xa0\x0f\x08\x8b\xa1\x84\xc1",
|
||||||
|
(const uint8_t *)"\x03\xf3\x6c\x7d\x0f\xb6\x15\xad\xa4\x3d\x71\x88\x58\x0f\x15\xeb\xda\x22\xd6\xf6\xb9\xb1\xa9\x2b\xff\x16\xc6\x93\x77\x99\xdc\xbc\x66"
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Debug private keys for v2 (previously called "new") scheme
|
Debug private keys for v2 (previously called "new") scheme
|
||||||
corresponding to pubkeys below as python hexstring array:
|
corresponding to pubkeys below as python hexstring array:
|
||||||
@ -81,8 +105,10 @@ static const uint8_t * const pubkey_v2[PUBKEYS] = {
|
|||||||
(const uint8_t *)"\x03\x66\x63\x5d\x99\x94\x17\xb6\x55\x66\x86\x6c\x65\x63\x0d\x97\x7a\x7a\xe7\x23\xfe\x5f\x6c\x4c\xd1\x7f\xa0\x0f\x08\x8b\xa1\x84\xc1",
|
(const uint8_t *)"\x03\x66\x63\x5d\x99\x94\x17\xb6\x55\x66\x86\x6c\x65\x63\x0d\x97\x7a\x7a\xe7\x23\xfe\x5f\x6c\x4c\xd1\x7f\xa0\x0f\x08\x8b\xa1\x84\xc1",
|
||||||
(const uint8_t *)"\x03\xf3\x6c\x7d\x0f\xb6\x15\xad\xa4\x3d\x71\x88\x58\x0f\x15\xeb\xda\x22\xd6\xf6\xb9\xb1\xa9\x2b\xff\x16\xc6\x93\x77\x99\xdc\xbc\x66"
|
(const uint8_t *)"\x03\xf3\x6c\x7d\x0f\xb6\x15\xad\xa4\x3d\x71\x88\x58\x0f\x15\xeb\xda\x22\xd6\xf6\xb9\xb1\xa9\x2b\xff\x16\xc6\x93\x77\x99\xdc\xbc\x66"
|
||||||
};
|
};
|
||||||
#else
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error NOT IMPLEMENTED
|
||||||
// These public keys are production keys
|
// These public keys are production keys
|
||||||
// - used in production devices
|
// - used in production devices
|
||||||
|
|
||||||
|
@ -44,10 +44,15 @@ endif
|
|||||||
@printf " MAKO $@\n"
|
@printf " MAKO $@\n"
|
||||||
$(Q)$(PYTHON) ../vendor/trezor-common/tools/cointool.py render $(MAKO_RENDER_FLAG) $@.mako
|
$(Q)$(PYTHON) ../vendor/trezor-common/tools/cointool.py render $(MAKO_RENDER_FLAG) $@.mako
|
||||||
|
|
||||||
|
ifeq ($(BOOTLOADER_QA), 0)
|
||||||
bl_data.h: bl_data.py bootloader.dat
|
bl_data.h: bl_data.py bootloader.dat
|
||||||
@printf " PYTHON bl_data.py\n"
|
@printf " PYTHON bl_data.py bootloader.dat\n"
|
||||||
$(Q)$(PYTHON) bl_data.py
|
$(Q)$(PYTHON) bl_data.py bootloader.dat
|
||||||
|
else
|
||||||
|
bl_data.h: bl_data.py bootloader_qa.dat
|
||||||
|
@printf " PYTHON bl_data.py bootloader_qa.dat\n"
|
||||||
|
$(Q)$(PYTHON) bl_data.py bootloader_qa.dat
|
||||||
|
endif
|
||||||
clean::
|
clean::
|
||||||
rm -f bl_data.h
|
rm -f bl_data.h
|
||||||
find -maxdepth 1 -name "*.mako" | sed 's/.mako$$//' | xargs rm -f
|
find -maxdepth 1 -name "*.mako" | sed 's/.mako$$//' | xargs rm -f
|
||||||
|
1
legacy/intermediate_fw/bootloader_qa.dat
Symbolic link
1
legacy/intermediate_fw/bootloader_qa.dat
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../firmware/bootloader_qa.dat
|
@ -30,6 +30,9 @@
|
|||||||
|
|
||||||
void memory_protect(void) {
|
void memory_protect(void) {
|
||||||
#if PRODUCTION
|
#if PRODUCTION
|
||||||
|
#if BOOTLOADER_QA
|
||||||
|
#error BOOTLOADER_QA must be built with PRODUCTION=0
|
||||||
|
#endif
|
||||||
// Reference STM32F205 Flash programming manual revision 5
|
// Reference STM32F205 Flash programming manual revision 5
|
||||||
// http://www.st.com/resource/en/programming_manual/cd00233952.pdf Section 2.6
|
// http://www.st.com/resource/en/programming_manual/cd00233952.pdf Section 2.6
|
||||||
// Option bytes
|
// Option bytes
|
||||||
@ -73,11 +76,16 @@ void memory_protect(void) {
|
|||||||
// example in STM32F427, where the protection bits are read correctly
|
// example in STM32F427, where the protection bits are read correctly
|
||||||
// from OPTION_BYTES and not form FLASH_OPCTR register.
|
// from OPTION_BYTES and not form FLASH_OPCTR register.
|
||||||
//
|
//
|
||||||
// Read protection is unaffected and always stays locked to the desired value.
|
// Read protection is set to level 2.
|
||||||
void memory_write_unlock(void) {
|
void memory_write_unlock(void) {
|
||||||
|
#if PRODUCTION
|
||||||
|
#if BOOTLOADER_QA
|
||||||
|
#error BOOTLOADER_QA must be built with PRODUCTION=0
|
||||||
|
#endif
|
||||||
flash_unlock_option_bytes();
|
flash_unlock_option_bytes();
|
||||||
flash_program_option_bytes(0x0FFFCCEC);
|
flash_program_option_bytes(0x0FFFCCEC);
|
||||||
flash_lock_option_bytes();
|
flash_lock_option_bytes();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int memory_bootloader_hash(uint8_t *hash) {
|
int memory_bootloader_hash(uint8_t *hash) {
|
||||||
|
@ -11,12 +11,14 @@ LEGACY_ROOT = Path(__file__).parent.parent.resolve()
|
|||||||
|
|
||||||
BOOTLOADER_BUILT = LEGACY_ROOT / "bootloader" / "bootloader.bin"
|
BOOTLOADER_BUILT = LEGACY_ROOT / "bootloader" / "bootloader.bin"
|
||||||
BOOTLOADER_IMAGE = LEGACY_ROOT / "firmware" / "bootloader.dat"
|
BOOTLOADER_IMAGE = LEGACY_ROOT / "firmware" / "bootloader.dat"
|
||||||
|
BOOTLOADER_QA_IMAGE = LEGACY_ROOT / "firmware" / "bootloader_qa.dat"
|
||||||
|
|
||||||
BOOTLOADER_VERSION = LEGACY_ROOT / "bootloader" / "version.h"
|
BOOTLOADER_VERSION = LEGACY_ROOT / "bootloader" / "version.h"
|
||||||
FIRMWARE_VERSION = LEGACY_ROOT / "firmware" / "version.h"
|
FIRMWARE_VERSION = LEGACY_ROOT / "firmware" / "version.h"
|
||||||
|
|
||||||
BL_CHECK_C = LEGACY_ROOT / "firmware" / "bl_check.c"
|
BL_CHECK_C = LEGACY_ROOT / "firmware" / "bl_check.c"
|
||||||
BL_CHECK_TXT = LEGACY_ROOT / "firmware" / "bl_check.txt"
|
BL_CHECK_TXT = LEGACY_ROOT / "firmware" / "bl_check.txt"
|
||||||
|
BL_CHECK_QA_TXT = LEGACY_ROOT / "firmware" / "bl_check_qa.txt"
|
||||||
|
|
||||||
BL_CHECK_PATTERN = """\
|
BL_CHECK_PATTERN = """\
|
||||||
if (0 ==
|
if (0 ==
|
||||||
@ -30,6 +32,13 @@ BL_CHECK_PATTERN = """\
|
|||||||
BL_CHECK_AUTO_BEGIN = " // BEGIN AUTO-GENERATED BOOTLOADER ENTRIES (bl_check.txt)\n"
|
BL_CHECK_AUTO_BEGIN = " // BEGIN AUTO-GENERATED BOOTLOADER ENTRIES (bl_check.txt)\n"
|
||||||
BL_CHECK_AUTO_END = " // END AUTO-GENERATED BOOTLOADER ENTRIES (bl_check.txt)\n"
|
BL_CHECK_AUTO_END = " // END AUTO-GENERATED BOOTLOADER ENTRIES (bl_check.txt)\n"
|
||||||
|
|
||||||
|
BL_CHECK_AUTO_QA_BEGIN = (
|
||||||
|
" // BEGIN AUTO-GENERATED QA BOOTLOADER ENTRIES (bl_check_qa.txt)\n"
|
||||||
|
)
|
||||||
|
BL_CHECK_AUTO_QA_END = (
|
||||||
|
" // END AUTO-GENERATED QA BOOTLOADER ENTRIES (bl_check_qa.txt)\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def cstrify(data: bytes) -> str:
|
def cstrify(data: bytes) -> str:
|
||||||
"""Convert bytes to C string literal.
|
"""Convert bytes to C string literal.
|
||||||
@ -53,25 +62,28 @@ def load_version(filename: Path) -> str:
|
|||||||
return "{major}.{minor}.{patch}".format(**vdict)
|
return "{major}.{minor}.{patch}".format(**vdict)
|
||||||
|
|
||||||
|
|
||||||
def load_hash_entries() -> dict[bytes, str]:
|
def load_hash_entries(txt_file) -> dict[bytes, str]:
|
||||||
"""Load hash entries from bl_check.txt"""
|
"""Load hash entries from bl_check.txt"""
|
||||||
return {
|
return {
|
||||||
bytes.fromhex(digest): comment
|
bytes.fromhex(digest): comment
|
||||||
for digest, comment in (
|
for digest, comment in (
|
||||||
line.split(" ", maxsplit=1)
|
line.split(" ", maxsplit=1) for line in txt_file.read_text().splitlines()
|
||||||
for line in BL_CHECK_TXT.read_text().splitlines()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def regenerate_bl_check(hash_entries: t.Iterable[tuple[bytes, str]]) -> None:
|
def regenerate_bl_check(
|
||||||
|
hash_entries: t.Iterable[tuple[bytes, str]],
|
||||||
|
begin,
|
||||||
|
end,
|
||||||
|
) -> None:
|
||||||
"""Regenerate bl_check.c with given hash entries."""
|
"""Regenerate bl_check.c with given hash entries."""
|
||||||
bl_check_new = []
|
bl_check_new = []
|
||||||
with open(BL_CHECK_C) as f:
|
with open(BL_CHECK_C) as f:
|
||||||
# read up to AUTO-BEGIN
|
# read up to AUTO-BEGIN
|
||||||
for line in f:
|
for line in f:
|
||||||
bl_check_new.append(line)
|
bl_check_new.append(line)
|
||||||
if line == BL_CHECK_AUTO_BEGIN:
|
if line == begin:
|
||||||
break
|
break
|
||||||
|
|
||||||
# generate new sections
|
# generate new sections
|
||||||
@ -86,7 +98,7 @@ def regenerate_bl_check(hash_entries: t.Iterable[tuple[bytes, str]]) -> None:
|
|||||||
|
|
||||||
# consume up to AUTO-END
|
# consume up to AUTO-END
|
||||||
for line in f:
|
for line in f:
|
||||||
if line == BL_CHECK_AUTO_END:
|
if line == end:
|
||||||
bl_check_new.append(line)
|
bl_check_new.append(line)
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -98,7 +110,14 @@ def regenerate_bl_check(hash_entries: t.Iterable[tuple[bytes, str]]) -> None:
|
|||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@click.option("-c", "--comment", help="Comment for the hash entry.")
|
@click.option("-c", "--comment", help="Comment for the hash entry.")
|
||||||
def main(comment: str | None) -> None:
|
@click.option(
|
||||||
|
"--qa",
|
||||||
|
is_flag=True,
|
||||||
|
show_default=True,
|
||||||
|
default=False,
|
||||||
|
help="Install as QA bootloader.",
|
||||||
|
)
|
||||||
|
def main(comment: str | None, qa: bool) -> None:
|
||||||
"""Insert a new bootloader image.
|
"""Insert a new bootloader image.
|
||||||
|
|
||||||
Takes bootloader/boootloader.dat, copies over firmware/bootloader.dat, and adds
|
Takes bootloader/boootloader.dat, copies over firmware/bootloader.dat, and adds
|
||||||
@ -108,7 +127,12 @@ def main(comment: str | None) -> None:
|
|||||||
digest = sha256(sha256(bl_bytes).digest()).digest()
|
digest = sha256(sha256(bl_bytes).digest()).digest()
|
||||||
click.echo("Bootloader digest: " + digest.hex())
|
click.echo("Bootloader digest: " + digest.hex())
|
||||||
|
|
||||||
entries = load_hash_entries()
|
txt_file = BL_CHECK_QA_TXT if qa else BL_CHECK_TXT
|
||||||
|
begin = BL_CHECK_AUTO_QA_BEGIN if qa else BL_CHECK_AUTO_BEGIN
|
||||||
|
end = BL_CHECK_AUTO_QA_END if qa else BL_CHECK_AUTO_END
|
||||||
|
image = BOOTLOADER_QA_IMAGE if qa else BOOTLOADER_IMAGE
|
||||||
|
|
||||||
|
entries = load_hash_entries(txt_file)
|
||||||
if digest in entries:
|
if digest in entries:
|
||||||
click.echo("Bootloader already in bl_check.txt: " + entries[digest])
|
click.echo("Bootloader already in bl_check.txt: " + entries[digest])
|
||||||
|
|
||||||
@ -119,18 +143,22 @@ def main(comment: str | None) -> None:
|
|||||||
comment = f"{bl_version} shipped with fw {fw_version}"
|
comment = f"{bl_version} shipped with fw {fw_version}"
|
||||||
|
|
||||||
# insert new bootloader
|
# insert new bootloader
|
||||||
with open(BL_CHECK_TXT, "a") as f:
|
with open(txt_file, "a") as f:
|
||||||
f.write(f"{digest.hex()} {comment}\n")
|
f.write(f"{digest.hex()} {comment}\n")
|
||||||
|
|
||||||
entries[digest] = comment
|
entries[digest] = comment
|
||||||
click.echo("Inserted new entry: " + comment)
|
click.echo("Inserted new entry: " + comment)
|
||||||
|
|
||||||
# rewrite bl_check.c
|
# rewrite bl_check.c
|
||||||
regenerate_bl_check(entries.items())
|
regenerate_bl_check(entries.items(), begin, end)
|
||||||
click.echo("Regenerated bl_check.c")
|
click.echo("Regenerated bl_check.c")
|
||||||
|
|
||||||
# overwrite bootloader.dat
|
# overwrite bootloader.dat
|
||||||
BOOTLOADER_IMAGE.write_bytes(bl_bytes)
|
image.write_bytes(bl_bytes)
|
||||||
|
|
||||||
|
if qa:
|
||||||
|
click.echo("Installed bootloader_qa.dat into firmware")
|
||||||
|
else:
|
||||||
click.echo("Installed bootloader.dat into firmware")
|
click.echo("Installed bootloader.dat into firmware")
|
||||||
|
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@ void mpu_config_bootloader(void) {
|
|||||||
|
|
||||||
// Never use in bootloader! Disables access to PPB (including MPU, NVIC, SCB)
|
// Never use in bootloader! Disables access to PPB (including MPU, NVIC, SCB)
|
||||||
void mpu_config_firmware(void) {
|
void mpu_config_firmware(void) {
|
||||||
#if PRODUCTION
|
#if PRODUCTION || BOOTLOADER_QA
|
||||||
// Disable MPU
|
// Disable MPU
|
||||||
MPU_CTRL = 0;
|
MPU_CTRL = 0;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user