mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-20 10:06:07 +00:00
Merge branch 'release/23.03'
Conflicts: legacy/firmware/version.h tests/ui_tests/fixtures.json
This commit is contained in:
commit
dce876069b
@ -41,27 +41,81 @@ ALPINE_TARBALL=${ALPINE_FILE:-alpine-minirootfs-$ALPINE_VERSION-$ALPINE_ARCH.tar
|
||||
NIX_VERSION=${NIX_VERSION:-2.4}
|
||||
CONTAINER_FS_URL=${CONTAINER_FS_URL:-"$ALPINE_CDN/v$ALPINE_RELEASE/releases/$ALPINE_ARCH/$ALPINE_TARBALL"}
|
||||
|
||||
VARIANTS_core=(0 1)
|
||||
VARIANTS_legacy=(0 1)
|
||||
function help_and_die() {
|
||||
echo "Usage: $0 [options] tag"
|
||||
echo "Options:"
|
||||
echo " --skip-bitcoinonly"
|
||||
echo " --skip-normal"
|
||||
echo " --skip-core"
|
||||
echo " --skip-legacy"
|
||||
echo " --repository path/to/repo"
|
||||
echo " --help"
|
||||
echo
|
||||
echo "Set PRODUCTION=0 to run non-production builds."
|
||||
exit 0
|
||||
}
|
||||
|
||||
if [ "$1" == "--skip-bitcoinonly" ]; then
|
||||
VARIANTS_core=(0)
|
||||
VARIANTS_legacy=(0)
|
||||
shift
|
||||
OPT_BUILD_CORE=1
|
||||
OPT_BUILD_LEGACY=1
|
||||
OPT_BUILD_NORMAL=1
|
||||
OPT_BUILD_BITCOINONLY=1
|
||||
|
||||
REPOSITORY="/local"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
help_and_die
|
||||
;;
|
||||
--skip-bitcoinonly)
|
||||
OPT_BUILD_BITCOINONLY=0
|
||||
shift
|
||||
;;
|
||||
--skip-normal)
|
||||
OPT_BUILD_NORMAL=0
|
||||
shift
|
||||
;;
|
||||
--skip-core)
|
||||
OPT_BUILD_CORE=0
|
||||
shift
|
||||
;;
|
||||
--skip-legacy)
|
||||
OPT_BUILD_LEGACY=0
|
||||
shift
|
||||
;;
|
||||
--repository)
|
||||
REPOSITORY="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
help_and_die
|
||||
fi
|
||||
|
||||
if [ "$1" == "--skip-core" ]; then
|
||||
VARIANTS_core=()
|
||||
shift
|
||||
variants=()
|
||||
if [ "$OPT_BUILD_NORMAL" -eq 1 ]; then
|
||||
variants+=(0)
|
||||
fi
|
||||
if [ "$OPT_BUILD_BITCOINONLY" -eq 1 ]; then
|
||||
variants+=(1)
|
||||
fi
|
||||
|
||||
if [ "$1" == "--skip-legacy" ]; then
|
||||
VARIANTS_legacy=()
|
||||
shift
|
||||
VARIANTS_core=()
|
||||
VARIANTS_legacy=()
|
||||
|
||||
if [ "$OPT_BUILD_CORE" -eq 1 ]; then
|
||||
VARIANTS_core=("${variants[@]}")
|
||||
fi
|
||||
if [ "$OPT_BUILD_LEGACY" -eq 1 ]; then
|
||||
VARIANTS_legacy=("${variants[@]}")
|
||||
fi
|
||||
|
||||
TAG=${1:-master}
|
||||
REPOSITORY=${2:-/local}
|
||||
TAG="$1"
|
||||
PRODUCTION=${PRODUCTION:-1}
|
||||
|
||||
|
||||
|
@ -21,6 +21,7 @@ CROSS_PORT_OPTS ?=
|
||||
PRODUCTION ?= 0
|
||||
PYOPT ?= 1
|
||||
BITCOIN_ONLY ?= 0
|
||||
BOOTLOADER_QA ?= 0
|
||||
TREZOR_MODEL ?= T
|
||||
TREZOR_MEMPERF ?= 0
|
||||
ADDRESS_SANITIZER ?= 0
|
||||
@ -178,7 +179,7 @@ build_reflash: ## build reflash firmware + reflash image
|
||||
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
|
||||
$(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" $(FIRMWARE_BUILD_DIR)/firmware.bin
|
||||
$(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" BOOTLOADER_QA="$(BOOTLOADER_QA)" $(FIRMWARE_BUILD_DIR)/firmware.bin
|
||||
|
||||
build_unix: templates ## build unix port
|
||||
$(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" PYOPT="0" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)"
|
||||
|
@ -5,11 +5,16 @@ import os
|
||||
import tools
|
||||
|
||||
BITCOIN_ONLY = ARGUMENTS.get('BITCOIN_ONLY', '0')
|
||||
PRODUCTION = ARGUMENTS.get('PRODUCTION', '0')
|
||||
BOOTLOADER_QA = ARGUMENTS.get('BOOTLOADER_QA', '0') == '1'
|
||||
EVERYTHING = BITCOIN_ONLY != '1'
|
||||
TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T')
|
||||
DMA2D = TREZOR_MODEL in ('T', )
|
||||
CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0))
|
||||
|
||||
if PRODUCTION != '1' and BOOTLOADER_QA:
|
||||
raise ValueError('Firmware variant for bootloader upgrade testing must be done with PRODUCTION=1')
|
||||
|
||||
FEATURE_FLAGS = {
|
||||
"RDI": True,
|
||||
"SECP256K1_ZKP": True, # required for trezor.crypto.curve.bip340 (BIP340/Taproot)
|
||||
@ -424,7 +429,7 @@ tools.add_font('MONO', FONT_MONO, CPPDEFINES_MOD, SOURCE_MOD)
|
||||
|
||||
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))
|
||||
|
||||
FEATURES_AVAILABLE = tools.configure_board(TREZOR_MODEL, FEATURES_WANTED, env, CPPDEFINES_MOD, SOURCE_TREZORHAL)
|
||||
|
||||
@ -782,7 +787,7 @@ obj_program.extend(
|
||||
' $SOURCE $TARGET', ))
|
||||
|
||||
|
||||
BOOTLOADER_SUFFIX = tools.get_model_identifier(TREZOR_MODEL)
|
||||
BOOTLOADER_SUFFIX = tools.get_model_identifier(TREZOR_MODEL) + ('_QA' if BOOTLOADER_QA else '')
|
||||
|
||||
obj_program.extend(
|
||||
env.Command(
|
||||
|
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
|
@ -110,6 +110,9 @@ trezorctl firmware-update -f build/legacy/firmware/firmware.bin
|
||||
|
||||
## Combining bootloader and firmware with various `PRODUCTION` settings, signed/unsigned
|
||||
|
||||
This is an issue before firmware 1.11.2, historical versions need to be built according
|
||||
to this table.
|
||||
|
||||
Not all combinations of bootloader and firmware will work. This depends on
|
||||
3 variables: PRODUCTION of bootloader, PRODUCTION of firmware, whether firmware is signed
|
||||
|
||||
@ -118,9 +121,9 @@ This table shows the result for bootloader 1.8.0+ and 1.9.1+:
|
||||
| Bootloader PRODUCTION | Firmware PRODUCTION | Is firmware officially signed? | Result |
|
||||
| ------------------------- | ----------------------- | ------------------------------ | ------------------------------------------------------------------------------------------ |
|
||||
| 1 | 1 | yes | works, official configuration |
|
||||
| 1 | 1 | no | hardfault in header.S when setting VTOR and stack |
|
||||
| 1 | 1 | no | hardfault in startup.S when setting VTOR and stack |
|
||||
| 0 | 1 | no | works, but don't forget to comment out `check_and_replace_bootloader`, otherwise it'll get overwritten |
|
||||
| 0 | 0 | no | hard fault because header.S doesn't set VTOR and stack right |
|
||||
| 0 | 0 | no | hard fault because startup.S doesn't set VTOR and stack right |
|
||||
| 1 | 0 | no | works |
|
||||
|
||||
The other three possibilities with signed firmware and `PRODUCTION!=0` for bootloader/firmware don't exist.
|
||||
|
@ -1,6 +1,5 @@
|
||||
ifneq ($(EMULATOR),1)
|
||||
OBJS += setup.o
|
||||
OBJS += startup.o
|
||||
OBJS += timer.o
|
||||
endif
|
||||
|
||||
|
@ -95,6 +95,13 @@ LDFLAGS += -L$(TOP_DIR) \
|
||||
|
||||
ZKP_PATH = $(TOP_DIR)vendor/secp256k1-zkp
|
||||
|
||||
# hw_model = T1B1 = 0x31423154
|
||||
HW_MODEL = 0x31423154
|
||||
CPUFLAGS += -DHW_MODEL=$(HW_MODEL)
|
||||
CPUFLAGS += -DHW_REVISION=0
|
||||
CFLAGS += -DHW_MODEL=$(HW_MODEL)
|
||||
CFLAGS += -DHW_REVISION=0
|
||||
|
||||
ifeq ($(EMULATOR),1)
|
||||
CFLAGS += -DEMULATOR=1
|
||||
|
||||
@ -156,6 +163,18 @@ CPUFLAGS += -DPRODUCTION=1
|
||||
$(info PRODUCTION=1)
|
||||
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)
|
||||
CFLAGS += -DDEBUG_RNG=1
|
||||
else
|
||||
|
@ -1 +0,0 @@
|
||||
T1 bootloader: verify firmware signatures based on SignMessage, add signature debugging
|
@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## 1.12.1 [February 2023]
|
||||
|
||||
### Fixed
|
||||
- Correctly distrust v2-signed firmwares.
|
||||
|
||||
|
||||
## 1.12.0 [internal only - January 2023]
|
||||
|
||||
### Added
|
||||
- T1 bootloader: verify firmware signatures based on SignMessage, add signature debugging [#2568]
|
||||
|
||||
### Fixed
|
||||
- Better way to debug T1 combinations of debug/production combinations of bootloader and firmware [#2423]
|
||||
|
||||
|
||||
## 1.11.0 [May 2022]
|
||||
|
||||
### Added
|
||||
@ -128,3 +143,5 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
[#1642]: https://github.com/trezor/trezor-firmware/pull/1642
|
||||
[#1884]: https://github.com/trezor/trezor-firmware/pull/1884
|
||||
[#2231]: https://github.com/trezor/trezor-firmware/pull/2231
|
||||
[#2423]: https://github.com/trezor/trezor-firmware/pull/2423
|
||||
[#2568]: https://github.com/trezor/trezor-firmware/pull/2568
|
||||
|
@ -1,5 +1,6 @@
|
||||
NAME = bootloader-unaligned
|
||||
|
||||
OBJS += startup.o
|
||||
OBJS += bootloader.o
|
||||
OBJS += usb.o
|
||||
|
||||
@ -18,7 +19,6 @@ OBJS += ../oled.small.o
|
||||
OBJS += ../random_delays.small.o
|
||||
OBJS += ../rng.small.o
|
||||
OBJS += ../setup.small.o
|
||||
OBJS += ../startup.o
|
||||
OBJS += ../supervise.small.o
|
||||
OBJS += ../timer.small.o
|
||||
OBJS += ../usb21_standard.small.o
|
||||
|
@ -161,6 +161,12 @@ int main(void) {
|
||||
if (SIG_OK != signed_firmware) {
|
||||
show_unofficial_warning(fingerprint);
|
||||
}
|
||||
#if !PRODUCTION && !BOOTLOADER_QA && !DEBUG_T1_SIGNATURES
|
||||
// try to avoid bricking board SWD debug by accident
|
||||
else {
|
||||
show_halt("Official firmware", "Won't run on debug device");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (SIG_OK != check_firmware_hashes(hdr)) {
|
||||
show_halt("Broken firmware", "detected.");
|
||||
|
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)
|
@ -20,18 +20,6 @@ memset_reg:
|
||||
.type reset_handler, STT_FUNC
|
||||
reset_handler:
|
||||
|
||||
#if PRODUCTION
|
||||
// we need to perform this in case an old bootloader (<1.8.0)
|
||||
// is starting the new firmware, these will be set incorrectly
|
||||
ldr r0, =0xE000ED08 // r0 = VTOR address
|
||||
ldr r1, =0x08010400 // r1 = FLASH_APP_START
|
||||
str r1, [r0] // assign
|
||||
ldr r0, =_stack // r0 = stack pointer
|
||||
msr msp, r0 // set stack pointer
|
||||
dsb
|
||||
isb
|
||||
#endif
|
||||
|
||||
ldr r0, =_stay_in_bootloader_flag_addr // r0 - address of storage for "stay in bootloader" flag
|
||||
ldr r11, [r0] // r11 - keep in register and hope it gets to main
|
||||
|
@ -186,7 +186,7 @@ static int should_keep_storage(int old_was_signed,
|
||||
|
||||
const image_header *new_hdr = (const image_header *)FW_HEADER;
|
||||
// new header must be signed by v3 signmessage/verifymessage scheme
|
||||
if (SIG_OK != signatures_ok(new_hdr, NULL, true)) return SIG_FAIL;
|
||||
if (SIG_OK != signatures_ok(new_hdr, NULL, sectrue)) return SIG_FAIL;
|
||||
// if the new header hashes don't match flash contents, erase storage
|
||||
if (SIG_OK != check_firmware_hashes(new_hdr)) return SIG_FAIL;
|
||||
|
||||
@ -401,7 +401,7 @@ static void rx_callback(usbd_device *dev, uint8_t ep) {
|
||||
flash_state = STATE_CHECK;
|
||||
const image_header *hdr = (const image_header *)FW_HEADER;
|
||||
// allow only v3 signmessage/verifymessage signature for new FW
|
||||
if (SIG_OK != signatures_ok(hdr, NULL, true)) {
|
||||
if (SIG_OK != signatures_ok(hdr, NULL, sectrue)) {
|
||||
send_msg_buttonrequest_firmwarecheck(dev);
|
||||
return;
|
||||
}
|
||||
@ -412,12 +412,20 @@ static void rx_callback(usbd_device *dev, uint8_t ep) {
|
||||
|
||||
if (flash_state == STATE_CHECK) {
|
||||
// use the firmware header from RAM
|
||||
const image_header *hdr = (const image_header *)FW_HEADER;
|
||||
image_header *hdr = (image_header *)FW_HEADER;
|
||||
|
||||
bool hash_check_ok;
|
||||
// show fingerprint of unsigned firmware
|
||||
// allow only v3 signmessage/verifymessage signatures
|
||||
if (SIG_OK != signatures_ok(hdr, NULL, true)) {
|
||||
if (SIG_OK != signatures_ok(hdr, NULL, sectrue)) {
|
||||
// clear invalid signatures
|
||||
hdr->sigindex1 = 0;
|
||||
hdr->sigindex2 = 0;
|
||||
hdr->sigindex3 = 0;
|
||||
memset(hdr->sig1, 0, sizeof(hdr->sig1));
|
||||
memset(hdr->sig2, 0, sizeof(hdr->sig2));
|
||||
memset(hdr->sig3, 0, sizeof(hdr->sig3));
|
||||
|
||||
if (msg_id != 0x001B) { // ButtonAck message (id 27)
|
||||
return;
|
||||
}
|
||||
|
@ -1,7 +1,3 @@
|
||||
#define VERSION_MAJOR 1
|
||||
#define VERSION_MINOR 11
|
||||
#define VERSION_PATCH 0
|
||||
|
||||
#define VERSION_MAJOR_CHAR "\x01"
|
||||
#define VERSION_MINOR_CHAR "\x0B"
|
||||
#define VERSION_PATCH_CHAR "\x00"
|
||||
#define VERSION_MINOR 12
|
||||
#define VERSION_PATCH 2
|
||||
|
@ -8,12 +8,12 @@
|
||||
1. **FW header hash is different from whole FW hash in the one output by cibuild**
|
||||
1. Run the emulator or device (make sure not to confuse which are you using for signing)
|
||||
1. Run `firmware_hash_sign_trezor.py ../firmware/trezor.bin ../firmware/trezor.bin.signed`
|
||||
1. Accept 3 signature requests on signing device
|
||||
1. This will show you a list of 3 signatures for 3 keys
|
||||
1. Accept 2 signature requests on signing device
|
||||
1. This will show you a list of 2 signatures for 3 keys
|
||||
1. It will output `../firmware.trezor.bin.signed`
|
||||
|
||||
By default the scripts uses the `[1, 2, 3]` sigindices, you can modify `sig_indices`
|
||||
inside to have different order or different keys (1 <= index <= 5 )
|
||||
By default the scripts uses the `[1, 2]` sigindices, you can modify `sig_indices`
|
||||
inside to have different order or different keys (1 <= index <= 3 )
|
||||
|
||||
Update FW on T1 either via `trezorctl device firmware-update` or
|
||||
`make flash_firmware_jlink`.
|
||||
|
@ -45,9 +45,9 @@ class Signatures:
|
||||
Patch signatures from signature_pairs.
|
||||
Requires filling signature_pairs beforehand.
|
||||
"""
|
||||
assert len(self.signature_pairs) == 3
|
||||
assert len(self.signature_pairs) <= 3
|
||||
|
||||
for i in range(3):
|
||||
for i in range(len(self.signature_pairs)):
|
||||
sigindex_ofs = self.sigindex_offsets[i]
|
||||
sig_ofs = self.sig_offsets[i]
|
||||
(sigindex, sig) = self.signature_pairs[i]
|
||||
|
@ -16,8 +16,8 @@ except IndexError:
|
||||
out_fw_fname = in_fw_fname + ".signed"
|
||||
|
||||
# These 3 keys will be used in this order to sign the FW
|
||||
# each index can be >= 1 and <= 5
|
||||
sig_indices = [1, 2, 3]
|
||||
# each index can be >= 1 and <= 3
|
||||
sig_indices = [1, 2]
|
||||
|
||||
print(f"Input trezor.bin file: {in_fw_fname}")
|
||||
print(f"Output signed trezor.bin file: {out_fw_fname}")
|
||||
|
@ -10,9 +10,9 @@ import ecdsa
|
||||
from fill_t1_fw_signatures import Signatures
|
||||
|
||||
secret_keys_hex = [
|
||||
"ba7994923c91771ad77c483f7d2b41f5506b82aa900e6f12edeae96c5c9f8f66",
|
||||
"81a825d359da7ec9534e6cf7dd190bdbad62e134265764a5ec3e63317b060a51",
|
||||
"37107a021e50ca3571102691606083f6a8d9cd600e35cd2c8e8f7b87796a045b",
|
||||
"4444444444444444444444444444444444444444444444444444444444444444",
|
||||
"4545454545454545454545454545454545454545454545454545454545454545",
|
||||
"bfc4bca9c9c228a16639d3503d999a733a439210b64cebe757a4fd03ca46a5c8",
|
||||
"5518381d95e93e8eb68a294354989906e3828f36b4556a2ad85d8333294eb1b7",
|
||||
"1d1d34168760dec092c9ff89377d8659076d2dfd95e0281719c15f90d067e211",
|
||||
]
|
||||
@ -46,9 +46,9 @@ print(f"Output signed trezor.bin file: {out_fw_fname}")
|
||||
|
||||
# Should be these public keys
|
||||
assert public_keys_hex == [
|
||||
"0391cdaf3cac08c2712ee1e88cd6d346eb2c798fdaf95c0eb6efeea0d7014dac87",
|
||||
"02186ff4e2b08bc5ae0e21a508f1ced48ff451eab7d794deb0b7cbe8efd729aba7",
|
||||
"0324009f0b398ca1c335fb17a1021c3f7fb0831ddb28348a4f058b149ea4c589a0",
|
||||
"032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991",
|
||||
"02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145",
|
||||
"03665f660a5052be7a95546a02179058d93d3e08a779734914594346075bb0afd4",
|
||||
"0366635d999417b65566866c65630d977a7ae723fe5f6c4cd17fa00f088ba184c1",
|
||||
"03f36c7d0fb615ada43d7188580f15ebda22d6f6b9b1a92bff16c6937799dcbc66",
|
||||
]
|
||||
|
62
legacy/debug_signing/sign_firmware_v3_signature.py
Normal file
62
legacy/debug_signing/sign_firmware_v3_signature.py
Normal file
@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import typing as t
|
||||
from hashlib import sha256
|
||||
from pathlib import Path
|
||||
|
||||
import click
|
||||
import ecdsa
|
||||
|
||||
from trezorlib.firmware.legacy import LegacyV2Firmware
|
||||
from trezorlib.firmware.models import TREZOR_ONE_V3_DEV
|
||||
|
||||
SECRET_KEYS = [
|
||||
ecdsa.SigningKey.from_string(bytes.fromhex(sk), curve=ecdsa.SECP256k1)
|
||||
for sk in (
|
||||
"ca8de06e1e93d101136fa6fbc41432c52b6530299dfe32808030ee8e679702f1",
|
||||
"dde47dd393f7d76f9b522bfa9760bc4543d2c3654491393774f54e066461fccb",
|
||||
"14ba98c5c5cb8f1b214c661f4046ec2288c34fe2e73e02f149ec3dd8dad07ae2",
|
||||
)
|
||||
]
|
||||
|
||||
PUBLIC_KEYS: list[ecdsa.VerifyingKey] = [sk.get_verifying_key() for sk in SECRET_KEYS]
|
||||
|
||||
# Should be these public keys
|
||||
assert [
|
||||
pk.to_string("compressed") for pk in PUBLIC_KEYS
|
||||
] == TREZOR_ONE_V3_DEV.firmware_keys
|
||||
|
||||
|
||||
def signmessage(digest: bytes, key: ecdsa.SigningKey) -> bytes:
|
||||
"""Sign via SignMessage"""
|
||||
btc_digest = b"\x18Bitcoin Signed Message:\n\x20" + digest
|
||||
final_digest = sha256(sha256(btc_digest).digest()).digest()
|
||||
return key.sign_digest_deterministic(final_digest, hashfunc=sha256)
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument("firmware", type=click.File("rb"))
|
||||
@click.option("-i", "--index", "indices", type=int, multiple=True)
|
||||
def cli(firmware: click.utils.LazyFile, indices: t.Sequence[int]) -> None:
|
||||
fw = LegacyV2Firmware.parse(firmware.read())
|
||||
if not indices:
|
||||
indices = [1, 2]
|
||||
|
||||
if len(indices) > 3:
|
||||
raise click.ClickException("Too many indices")
|
||||
|
||||
digest = fw.digest()
|
||||
for i, idx in enumerate(indices):
|
||||
sk = SECRET_KEYS[idx - 1]
|
||||
sig = signmessage(digest, sk)
|
||||
|
||||
fw.header.v1_key_indexes[i] = idx
|
||||
fw.header.v1_signatures[i] = sig
|
||||
|
||||
new_fw = Path(firmware.name).with_suffix(".signed.bin")
|
||||
new_fw.write_bytes(fw.build())
|
||||
click.echo(f"Wrote signed firmware to {new_fw}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
@ -3,6 +3,7 @@ APPVER = 1.0.0
|
||||
NAME = demo
|
||||
|
||||
OBJS += demo.o
|
||||
OBJS += startup.o
|
||||
|
||||
OBJS += ../vendor/trezor-crypto/bignum.o
|
||||
OBJS += ../vendor/trezor-crypto/bip32.o
|
||||
|
1
legacy/demo/startup.S
Symbolic link
1
legacy/demo/startup.S
Symbolic link
@ -0,0 +1 @@
|
||||
../bootloader/startup.S
|
@ -1 +0,0 @@
|
||||
Show full Stellar address and QR code.
|
@ -1 +0,0 @@
|
||||
Support Ledger Live legacy derivation path "m/44'/coin_type'/0'/account"
|
@ -1 +0,0 @@
|
||||
Do not convert bech32 addresses to uppercase in QR code to increase compatibility
|
@ -1 +0,0 @@
|
||||
Do not allow access to SLIP25 paths.
|
@ -1 +0,0 @@
|
||||
Wrap long Ethereum fee to next line if it does not fit.
|
@ -1 +0,0 @@
|
||||
Show fee rate when replacing transaction
|
@ -1 +0,0 @@
|
||||
Extend decimals of fee rate to 2 digits
|
@ -1 +0,0 @@
|
||||
Display only sat instead of sat BTC
|
@ -1 +0,0 @@
|
||||
T1 bootloader: verify firmware signatures based on SignMessage, add signature debugging
|
@ -1 +0,0 @@
|
||||
Allow proposed Casa m/45' multisig paths for Bitcoin and Ethereum.
|
@ -1 +0,0 @@
|
||||
Support native SegWit external inputs with non-ownership proof.
|
@ -1 +0,0 @@
|
||||
Implement SLIP-0019 proofs of ownership for native SegWit.
|
@ -1 +0,0 @@
|
||||
Implement `serialize` option in SignTx.
|
@ -1 +0,0 @@
|
||||
Implement SLIP-0025 coinjoin accounts.
|
@ -1 +0,0 @@
|
||||
Implement coinjoin signing.
|
@ -1 +0,0 @@
|
||||
Increase 'SignIdentity.challenge_hidden' max_size to 512 bytes
|
@ -1 +0,0 @@
|
||||
Match and validate script type of change-outputs in Bitcoin signing.
|
@ -4,7 +4,36 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## [1.11.2] (17th August 2022)
|
||||
## 1.12.1 [15th March 2023]
|
||||
|
||||
### Added
|
||||
- Support Ledger Live legacy derivation path `m/44'/coin_type'/0'/account`. [#1749]
|
||||
- Show fee rate when replacing transaction. [#2442]
|
||||
- T1 bootloader: verify firmware signatures based on SignMessage, add signature debugging. [#2568]
|
||||
- Allow proposed Casa m/45' multisig paths for Bitcoin and Ethereum. [#2682]
|
||||
- Implement SLIP-0025 coinjoin accounts. [#2718]
|
||||
- Implement `serialize` option in SignTx. [#2718]
|
||||
- Support native SegWit external inputs with non-ownership proof. [#2718]
|
||||
- Implement SLIP-0019 proofs of ownership for native SegWit. [#2718]
|
||||
- Implement coinjoin signing. [#2718]
|
||||
|
||||
### Changed
|
||||
- Do not convert bech32 addresses to uppercase in QR code to increase compatibility. [#2190]
|
||||
- Extend decimals of fee rate to 2 digits. [#2486]
|
||||
- Display only sat instead of sat BTC. [#2487]
|
||||
- Increase `SignIdentity.challenge_hidden` max_size to 512 bytes. [#2743]
|
||||
- Included bootloader 1.12.1.
|
||||
|
||||
### Fixed
|
||||
- Bootloader VTOR and FW handover fix.
|
||||
- Show full Stellar address and QR code. [#1453]
|
||||
- Wrap long Ethereum fee to next line if it does not fit. [#2373]
|
||||
|
||||
### Security
|
||||
- Match and validate script type of change-outputs in Bitcoin signing.
|
||||
|
||||
|
||||
## 1.11.2 [17th August 2022]
|
||||
|
||||
### Added
|
||||
- Show the fee rate on the signing confirmation screen. [#2249]
|
||||
@ -522,6 +551,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
[#1369]: https://github.com/trezor/trezor-firmware/pull/1369
|
||||
[#1402]: https://github.com/trezor/trezor-firmware/pull/1402
|
||||
[#1415]: https://github.com/trezor/trezor-firmware/pull/1415
|
||||
[#1453]: https://github.com/trezor/trezor-firmware/pull/1453
|
||||
[#1461]: https://github.com/trezor/trezor-firmware/pull/1461
|
||||
[#1491]: https://github.com/trezor/trezor-firmware/pull/1491
|
||||
[#1518]: https://github.com/trezor/trezor-firmware/pull/1518
|
||||
@ -538,6 +568,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
[#1705]: https://github.com/trezor/trezor-firmware/pull/1705
|
||||
[#1710]: https://github.com/trezor/trezor-firmware/pull/1710
|
||||
[#1743]: https://github.com/trezor/trezor-firmware/pull/1743
|
||||
[#1749]: https://github.com/trezor/trezor-firmware/pull/1749
|
||||
[#1755]: https://github.com/trezor/trezor-firmware/pull/1755
|
||||
[#1765]: https://github.com/trezor/trezor-firmware/pull/1765
|
||||
[#1767]: https://github.com/trezor/trezor-firmware/pull/1767
|
||||
@ -559,9 +590,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
[#2115]: https://github.com/trezor/trezor-firmware/pull/2115
|
||||
[#2144]: https://github.com/trezor/trezor-firmware/pull/2144
|
||||
[#2181]: https://github.com/trezor/trezor-firmware/pull/2181
|
||||
[#2190]: https://github.com/trezor/trezor-firmware/pull/2190
|
||||
[#2239]: https://github.com/trezor/trezor-firmware/pull/2239
|
||||
[#2249]: https://github.com/trezor/trezor-firmware/pull/2249
|
||||
[#2261]: https://github.com/trezor/trezor-firmware/pull/2261
|
||||
[#2289]: https://github.com/trezor/trezor-firmware/pull/2289
|
||||
[#2373]: https://github.com/trezor/trezor-firmware/pull/2373
|
||||
[#2394]: https://github.com/trezor/trezor-firmware/pull/2394
|
||||
[#2422]: https://github.com/trezor/trezor-firmware/pull/2422
|
||||
[#2433]: https://github.com/trezor/trezor-firmware/pull/2433
|
||||
[#2442]: https://github.com/trezor/trezor-firmware/pull/2442
|
||||
[#2486]: https://github.com/trezor/trezor-firmware/pull/2486
|
||||
[#2487]: https://github.com/trezor/trezor-firmware/pull/2487
|
||||
[#2568]: https://github.com/trezor/trezor-firmware/pull/2568
|
||||
[#2682]: https://github.com/trezor/trezor-firmware/pull/2682
|
||||
[#2718]: https://github.com/trezor/trezor-firmware/pull/2718
|
||||
[#2743]: https://github.com/trezor/trezor-firmware/pull/2743
|
||||
|
@ -40,6 +40,7 @@ OBJS += usb.o
|
||||
OBJS += bl_check.o
|
||||
OBJS += otp.o
|
||||
OBJS += header.o
|
||||
OBJS += startup.o
|
||||
endif
|
||||
|
||||
OBJS += messages.o
|
||||
@ -172,9 +173,15 @@ endif
|
||||
@printf " MAKO $@\n"
|
||||
$(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
|
||||
@printf " PYTHON bl_data.py\n"
|
||||
$(Q)$(PYTHON) bl_data.py
|
||||
@printf " PYTHON bl_data.py bootloader.dat\n"
|
||||
$(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
|
||||
|
||||
|
@ -26,6 +26,87 @@
|
||||
#include "memory.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,
|
||||
"\x21\xb9\x49\xf4\xf5\xfd\x9f\x3a\x7d\x63\x43\xd1\x07\x6f\x96\x0f"
|
||||
"\xb4\x54\x18\x19\x65\x31\xb9\xf2\x97\x4a\x68\xed\xe8\xdb\x2e\xa1",
|
||||
32))
|
||||
return 1; // 1.12.0 shipped with fw 1.12.0
|
||||
if (0 ==
|
||||
memcmp(hash,
|
||||
"\xb4\xe5\x92\x44\x18\x5c\xe1\xcd\x6c\x8f\x59\x03\x10\x37\x02\x18"
|
||||
"\x50\x9c\x39\x32\x26\xf0\x07\x4b\x8c\xf6\xad\xed\xb3\xcd\x4d\x55",
|
||||
32))
|
||||
return 1; // 1.12.1 shipped with fw 1.12.1
|
||||
// END AUTO-GENERATED QA BOOTLOADER ENTRIES (bl_check_qa.txt)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PRODUCTION
|
||||
|
||||
static int known_bootloader(int r, const uint8_t *hash) {
|
||||
@ -138,6 +219,20 @@ 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",
|
||||
32))
|
||||
return 1; // 1.11.0 shipped with fw 1.11.1
|
||||
// BEGIN AUTO-GENERATED BOOTLOADER ENTRIES (bl_check.txt)
|
||||
if (0 ==
|
||||
memcmp(hash,
|
||||
"\xe9\xec\x8f\xa2\xfe\xfa\xd2\xb3\xb6\xb7\xc4\xab\x76\x69\x1a\x33"
|
||||
"\x61\xb3\xee\xfb\x8a\x0d\xda\xa3\x4d\x7e\x45\xb6\x3b\x3d\x56\x64",
|
||||
32))
|
||||
return 1; // 1.12.0 shipped with fw 1.12.0
|
||||
if (0 ==
|
||||
memcmp(hash,
|
||||
"\x94\xf1\xc9\x0d\xb2\x8d\xb1\xf8\xce\x5d\xca\x96\x69\x76\x34\x36"
|
||||
"\x58\xf5\xda\xde\xe8\x38\x34\x98\x7c\x8b\x04\x9c\x49\xd1\xed\xd0",
|
||||
32))
|
||||
return 1; // 1.12.1 shipped with fw 1.12.1
|
||||
// END AUTO-GENERATED BOOTLOADER ENTRIES (bl_check.txt)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@ -149,7 +244,7 @@ static int known_bootloader(int r, const uint8_t *hash) {
|
||||
* @param shutdown_on_replace: if true, shuts down device instead of return
|
||||
*/
|
||||
void check_and_replace_bootloader(bool shutdown_on_replace) {
|
||||
#if PRODUCTION
|
||||
#if PRODUCTION || BOOTLOADER_QA
|
||||
uint8_t hash[32] = {0};
|
||||
int r = memory_bootloader_hash(hash);
|
||||
|
||||
|
2
legacy/firmware/bl_check.txt
Normal file
2
legacy/firmware/bl_check.txt
Normal file
@ -0,0 +1,2 @@
|
||||
e9ec8fa2fefad2b3b6b7c4ab76691a3361b3eefb8a0ddaa34d7e45b63b3d5664 1.12.0 shipped with fw 1.12.0
|
||||
94f1c90db28db1f8ce5dca966976343658f5dadee83834987c8b049c49d1edd0 1.12.1 shipped with fw 1.12.1
|
2
legacy/firmware/bl_check_qa.txt
Normal file
2
legacy/firmware/bl_check_qa.txt
Normal file
@ -0,0 +1,2 @@
|
||||
21b949f4f5fd9f3a7d6343d1076f960fb45418196531b9f2974a68ede8db2ea1 1.12.0 shipped with fw 1.12.0
|
||||
b4e59244185ce1cd6c8f590310370218509c393226f0074b8cf6adedb3cd4d55 1.12.1 shipped with fw 1.12.1
|
@ -1,7 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
from hashlib import sha256
|
||||
|
||||
fn = "bootloader.dat"
|
||||
fn = sys.argv[1]
|
||||
|
||||
print("Embedding bootloader", fn)
|
||||
|
||||
data = open(fn, "rb").read()
|
||||
if len(data) > 32768:
|
||||
@ -18,18 +21,19 @@ 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_data[32768] = {{{bl_data}}};\n")
|
||||
|
||||
# make sure the last item listed in known_bootloader function
|
||||
# is our bootloader
|
||||
with open("bl_check.c", "rt") as f:
|
||||
hashes = []
|
||||
for l in f.readlines():
|
||||
if not len(l) >= 78 or not l.startswith(' "\\x'):
|
||||
continue
|
||||
l = l[14:78]
|
||||
h = ""
|
||||
for i in range(0, len(l), 4):
|
||||
h += l[i + 2 : i + 4]
|
||||
hashes.append(h)
|
||||
check = hashes[-2] + hashes[-1]
|
||||
if check != bh.hex():
|
||||
raise Exception("bootloader hash not listed in bl_check.c")
|
||||
if fn != "bootloader_qa.dat":
|
||||
# make sure the last item listed in known_bootloader function
|
||||
# is our bootloader
|
||||
with open("bl_check.c", "rt") as f:
|
||||
hashes = []
|
||||
for l in f.readlines():
|
||||
if not len(l) >= 78 or not l.startswith(' "\\x'):
|
||||
continue
|
||||
l = l[14:78]
|
||||
h = ""
|
||||
for i in range(0, len(l), 4):
|
||||
h += l[i + 2 : i + 4]
|
||||
hashes.append(h)
|
||||
check = hashes[-2] + hashes[-1]
|
||||
if check != bh.hex():
|
||||
raise Exception("bootloader hash not listed in bl_check.c")
|
||||
|
Binary file not shown.
BIN
legacy/firmware/bootloader_qa.dat
Normal file
BIN
legacy/firmware/bootloader_qa.dat
Normal file
Binary file not shown.
@ -24,8 +24,8 @@ bool get_features(Features *resp) {
|
||||
#else
|
||||
const image_header *hdr =
|
||||
(const image_header *)FLASH_PTR(FLASH_FWHEADER_START);
|
||||
// only v3 signatures count as signed
|
||||
if (SIG_OK == signatures_ok(hdr, NULL, true)) {
|
||||
// allow both v2 and v3 signatures
|
||||
if (SIG_OK == signatures_match(hdr, NULL)) {
|
||||
strlcpy(resp->fw_vendor, "SatoshiLabs", sizeof(resp->fw_vendor));
|
||||
} else {
|
||||
strlcpy(resp->fw_vendor, "UNSAFE, DO NOT USE!", sizeof(resp->fw_vendor));
|
||||
|
@ -20,7 +20,10 @@ g_header:
|
||||
.byte FIX_VERSION_MINOR // fix_vminor
|
||||
.byte FIX_VERSION_PATCH // fix_vpatch
|
||||
.byte 0 // fix_vbuild
|
||||
. = . + 8 // reserved
|
||||
.word HW_MODEL // type of the designated hardware
|
||||
.byte HW_REVISION // revision of the designated hardware
|
||||
.byte 0 // monotonic version placeholder - not used in legacy
|
||||
. = . + 2 // reserved
|
||||
. = . + 512 // hash1 ... hash16
|
||||
. = . + 64 // sig1
|
||||
. = . + 64 // sig2
|
||||
|
107
legacy/firmware/startup.S
Normal file
107
legacy/firmware/startup.S
Normal file
@ -0,0 +1,107 @@
|
||||
.syntax unified
|
||||
|
||||
.text
|
||||
|
||||
.global memset_reg
|
||||
.type memset_reg, STT_FUNC
|
||||
memset_reg:
|
||||
// call with the following (note that the arguments are not validated prior to use):
|
||||
// r0 - address of first word to write (inclusive)
|
||||
// r1 - address of first word following the address in r0 to NOT write (exclusive)
|
||||
// r2 - word value to be written
|
||||
// both addresses in r0 and r1 needs to be divisible by 4!
|
||||
.L_loop_begin:
|
||||
str r2, [r0], 4 // store the word in r2 to the address in r0, post-indexed
|
||||
cmp r0, r1
|
||||
bne .L_loop_begin
|
||||
bx lr
|
||||
|
||||
.global reset_handler
|
||||
.type reset_handler, STT_FUNC
|
||||
reset_handler:
|
||||
|
||||
// We need to perform VTOR setup case an old bootloader (<1.8.0)
|
||||
// is starting the new firmware, these will be set incorrectly.
|
||||
|
||||
// To make development easier, set only if we are in privileged
|
||||
// mode. This resolves annoying combinations of PRODUCTION
|
||||
// settings for bootloader and FW.
|
||||
// Normally only signed firmware will let bootloader start FW
|
||||
// in privileged mode (PRODUCTION=1 variants with signed everything).
|
||||
// But with devel bootloader we let FW start in privileged mode
|
||||
// and let's do the check if we can set VTOR without fault
|
||||
|
||||
// These two instructions are just for debug testing how unprivileged
|
||||
// FW is handled
|
||||
//mov r0, 1
|
||||
//msr control, r0 // set unprivileged
|
||||
|
||||
ldr sp, =_stack // setup stack
|
||||
|
||||
// are we privileged? if so, fix VTOR, otherwise skip
|
||||
mrs r3, control
|
||||
and r3, r3, #1
|
||||
cmp r3, #1
|
||||
beq .setup_as_unprivileged
|
||||
|
||||
ldr r0, =0xE000ED08 // r0 = VTOR address
|
||||
ldr r1, =0x08010400 // r1 = FLASH_APP_START
|
||||
str r1, [r0] // assign
|
||||
dsb
|
||||
isb
|
||||
|
||||
.setup_as_unprivileged:
|
||||
ldr r0, =_ram_start // r0 - point to beginning of SRAM
|
||||
ldr r1, =_ram_end // r1 - point to byte after the end of SRAM
|
||||
ldr r2, =0 // r2 - the byte-sized value to be written
|
||||
bl memset_reg
|
||||
|
||||
// copy .data section from flash to SRAM
|
||||
ldr r0, =_data // dst addr
|
||||
ldr r1, =_data_loadaddr // src addr
|
||||
ldr r2, =_data_size // length in bytes
|
||||
bl memcpy
|
||||
|
||||
// enter the application code
|
||||
bl main
|
||||
|
||||
// shutdown if the application code returns
|
||||
b shutdown
|
||||
|
||||
.global shutdown
|
||||
.type shutdown, STT_FUNC
|
||||
shutdown:
|
||||
cpsid f
|
||||
ldr r0, =0
|
||||
mov r1, r0
|
||||
mov r2, r0
|
||||
mov r3, r0
|
||||
mov r4, r0
|
||||
mov r5, r0
|
||||
mov r6, r0
|
||||
mov r7, r0
|
||||
mov r8, r0
|
||||
mov r9, r0
|
||||
mov r10, r0
|
||||
mov r11, r0
|
||||
mov r12, r0
|
||||
ldr lr, =0xffffffff
|
||||
ldr r0, =_ram_start
|
||||
ldr r1, =_ram_end
|
||||
// set to value in r2
|
||||
bl memset_reg
|
||||
b . // loop forever
|
||||
|
||||
.ltorg // dump literal pool (for the ldr ...,=... commands above)
|
||||
|
||||
.global sv_call_handler
|
||||
.type sv_call_handler, STT_FUNC
|
||||
|
||||
sv_call_handler:
|
||||
tst lr, #4
|
||||
ite eq
|
||||
mrseq r0, msp
|
||||
mrsne r0, psp
|
||||
b svc_handler_main
|
||||
|
||||
.end
|
@ -3,5 +3,5 @@
|
||||
#define VERSION_PATCH 2
|
||||
|
||||
#define FIX_VERSION_MAJOR 1
|
||||
#define FIX_VERSION_MINOR 11
|
||||
#define FIX_VERSION_MINOR 12
|
||||
#define FIX_VERSION_PATCH 0
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "fw_signatures.h"
|
||||
#include "memory.h"
|
||||
#include "memzero.h"
|
||||
#include "secbool.h"
|
||||
#include "secp256k1.h"
|
||||
#include "sha2.h"
|
||||
|
||||
@ -40,9 +41,10 @@ const uint32_t FIRMWARE_MAGIC_NEW = 0x465a5254; // TRZF
|
||||
*
|
||||
* Latest scheme v3 ref: https://github.com/trezor/trezor-firmware/issues/2513
|
||||
*/
|
||||
#define PUBKEYS 5
|
||||
#define PUBKEYS_V3 3
|
||||
#define PUBKEYS_V2 5
|
||||
|
||||
#if DEBUG_T1_SIGNATURES
|
||||
#if DEBUG_T1_SIGNATURES || BOOTLOADER_QA
|
||||
|
||||
// Make build explode if combining debug sigs with production
|
||||
#if PRODUCTION
|
||||
@ -54,12 +56,10 @@ const uint32_t FIRMWARE_MAGIC_NEW = 0x465a5254; // TRZF
|
||||
// "table table table table table table table table table table table advance"
|
||||
// the "SignMessage"-style public keys, third signing scheme
|
||||
// See legacy/debug_signing/README.md
|
||||
static const uint8_t * const pubkey_v3[PUBKEYS] = {
|
||||
static const uint8_t * const pubkey_v3[PUBKEYS_V3] = {
|
||||
(const uint8_t *)"\x03\x73\x08\xe1\x40\x77\x16\x1c\x36\x5d\xea\x0f\x5c\x80\xaa\x6c\x5d\xba\x34\x71\x9e\x82\x5b\xd2\x3a\xe5\xf7\xe7\xd2\x98\x8a\xdb\x0f",
|
||||
(const uint8_t *)"\x03\x9c\x1b\x24\x60\xe3\x43\x71\x2e\x98\x2e\x07\x32\xe7\xed\x17\xf6\x0d\xe4\xc9\x33\x06\x5b\x71\x70\xd9\x9c\x6e\x7f\xe7\xcc\x7f\x4b",
|
||||
(const uint8_t *)"\x03\x15\x2b\x37\xfd\xf1\x26\x11\x12\x74\xc8\x94\xc3\x48\xdc\xc9\x75\xb5\x7c\x11\x5e\xe2\x4c\xeb\x19\xb5\x19\x0a\xc7\xf7\xb6\x51\x73",
|
||||
(const uint8_t *)"\x02\x83\x91\x8a\xbf\x1b\x6e\x1d\x2a\x1d\x08\xea\x29\xc9\xd2\xae\x2e\x97\xe3\xc0\x1f\x4e\xa8\x59\x2b\x08\x8d\x28\x03\x5b\x44\x4e\xd0",
|
||||
(const uint8_t *)"\x03\xc6\x83\x63\x85\x07\x8a\x18\xe8\x1d\x74\x77\x68\x1e\x0d\x30\x86\x66\xf3\x99\x59\x4b\xe9\xe8\xab\xcb\x45\x58\xa6\xe2\x47\x32\xfc"
|
||||
};
|
||||
|
||||
// the "new", or second signing scheme keys
|
||||
@ -68,35 +68,34 @@ static const uint8_t * const pubkey_v3[PUBKEYS] = {
|
||||
Debug private keys for v2 (previously called "new") scheme
|
||||
corresponding to pubkeys below as python hexstring array:
|
||||
|
||||
['ba7994923c91771ad77c483f7d2b41f5506b82aa900e6f12edeae96c5c9f8f66',
|
||||
'81a825d359da7ec9534e6cf7dd190bdbad62e134265764a5ec3e63317b060a51',
|
||||
'37107a021e50ca3571102691606083f6a8d9cd600e35cd2c8e8f7b87796a045b',
|
||||
['4444444444444444444444444444444444444444444444444444444444444444',
|
||||
'4545454545454545454545454545454545454545454545454545454545454545',
|
||||
'bfc4bca9c9c228a16639d3503d999a733a439210b64cebe757a4fd03ca46a5c8',
|
||||
'5518381d95e93e8eb68a294354989906e3828f36b4556a2ad85d8333294eb1b7',
|
||||
'1d1d34168760dec092c9ff89377d8659076d2dfd95e0281719c15f90d067e211']
|
||||
*/
|
||||
static const uint8_t * const pubkey_v2[PUBKEYS] = {
|
||||
(const uint8_t *)"\x03\x91\xcd\xaf\x3c\xac\x08\xc2\x71\x2e\xe1\xe8\x8c\xd6\xd3\x46\xeb\x2c\x79\x8f\xda\xf9\x5c\x0e\xb6\xef\xee\xa0\xd7\x01\x4d\xac\x87",
|
||||
(const uint8_t *)"\x02\x18\x6f\xf4\xe2\xb0\x8b\xc5\xae\x0e\x21\xa5\x08\xf1\xce\xd4\x8f\xf4\x51\xea\xb7\xd7\x94\xde\xb0\xb7\xcb\xe8\xef\xd7\x29\xab\xa7",
|
||||
(const uint8_t *)"\x03\x24\x00\x9f\x0b\x39\x8c\xa1\xc3\x35\xfb\x17\xa1\x02\x1c\x3f\x7f\xb0\x83\x1d\xdb\x28\x34\x8a\x4f\x05\x8b\x14\x9e\xa4\xc5\x89\xa0",
|
||||
(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
|
||||
|
||||
static const uint8_t * const pubkey_v2[PUBKEYS_V2] = {
|
||||
(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_T1_SIGNATURES is now 0
|
||||
// These public keys are production keys
|
||||
// - used in production devices
|
||||
|
||||
// the "SignMessage"-style public keys, third signing scheme
|
||||
static const uint8_t * const pubkey_v3[PUBKEYS] = {
|
||||
(const uint8_t *)"\x02\xd5\x71\xb7\xf1\x48\xc5\xe4\x23\x2c\x38\x14\xf7\x77\xd8\xfa\xea\xf1\xa8\x42\x16\xc7\x8d\x56\x9b\x71\x04\x1f\xfc\x76\x8a\x5b\x2d",
|
||||
(const uint8_t *)"\x03\x63\x27\x9c\x0c\x08\x66\xe5\x0c\x05\xc7\x99\xd3\x2b\xd6\xba\xb0\x18\x8b\x6d\xe0\x65\x36\xd1\x10\x9d\x2e\xd9\xce\x76\xcb\x33\x5c",
|
||||
(const uint8_t *)"\x02\x43\xae\xdb\xb6\xf7\xe7\x1c\x56\x3f\x8e\xd2\xef\x64\xec\x99\x81\x48\x25\x19\xe7\xef\x4f\x4a\xa9\x8b\x27\x85\x4e\x8c\x49\x12\x6d",
|
||||
(const uint8_t *)"\x02\x87\x7c\x39\xfd\x7c\x62\x23\x7e\x03\x82\x35\xe9\xc0\x75\xda\xb2\x61\x63\x0f\x78\xee\xb8\xed\xb9\x24\x87\x15\x9f\xff\xed\xfd\xf6",
|
||||
(const uint8_t *)"\x03\x73\x84\xc5\x1a\xe8\x1a\xdd\x0a\x52\x3a\xdb\xb1\x86\xc9\x1b\x90\x6f\xfb\x64\xc2\xc7\x65\x80\x2b\xf2\x6d\xbd\x13\xbd\xf1\x2c\x31"
|
||||
static const uint8_t * const pubkey_v3[PUBKEYS_V3] = {
|
||||
(const uint8_t *)"\x03\x23\x00\xc1\xbb\x45\x39\xfc\xbf\xca\x25\x90\xbd\xa3\xdd\x20\x93\x82\x6f\x4a\xe4\x37\xbd\xde\xcc\x1a\x2e\x72\x52\x07\x64\xff\x7a",
|
||||
(const uint8_t *)"\x02\x33\xba\xea\xeb\xc9\x4a\x2a\x3e\x8b\x11\xf3\x9a\x71\x33\xdb\xf4\x27\xbe\x29\x2f\xcb\xce\xb8\x87\xd7\x1e\xf5\x1e\x85\x39\x5a\x19",
|
||||
(const uint8_t *)"\x03\x57\x09\x1f\xa2\x54\xb5\x52\x33\xd0\xbb\x4c\x48\xe1\x06\xc9\x1b\x92\xfd\x07\x88\xeb\xed\x9d\x3a\x91\x67\x19\xf4\x4c\x76\xc0\x15"
|
||||
};
|
||||
|
||||
// the "new", or second signing scheme keys
|
||||
static const uint8_t * const pubkey_v2[PUBKEYS] = {
|
||||
static const uint8_t * const pubkey_v2[PUBKEYS_V2] = {
|
||||
(const uint8_t *)"\x02\xd5\x71\xb7\xf1\x48\xc5\xe4\x23\x2c\x38\x14\xf7\x77\xd8\xfa\xea\xf1\xa8\x42\x16\xc7\x8d\x56\x9b\x71\x04\x1f\xfc\x76\x8a\x5b\x2d",
|
||||
(const uint8_t *)"\x03\x63\x27\x9c\x0c\x08\x66\xe5\x0c\x05\xc7\x99\xd3\x2b\xd6\xba\xb0\x18\x8b\x6d\xe0\x65\x36\xd1\x10\x9d\x2e\xd9\xce\x76\xcb\x33\x5c",
|
||||
(const uint8_t *)"\x02\x43\xae\xdb\xb6\xf7\xe7\x1c\x56\x3f\x8e\xd2\xef\x64\xec\x99\x81\x48\x25\x19\xe7\xef\x4f\x4a\xa9\x8b\x27\x85\x4e\x8c\x49\x12\x6d",
|
||||
@ -105,8 +104,6 @@ static const uint8_t * const pubkey_v2[PUBKEYS] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#define SIGNATURES 3
|
||||
|
||||
#define FLASH_META_START 0x08008000
|
||||
#define FLASH_META_CODELEN (FLASH_META_START + 0x0004)
|
||||
#define FLASH_META_SIGINDEX1 (FLASH_META_START + 0x0008)
|
||||
@ -171,28 +168,36 @@ bool firmware_present_new(void) {
|
||||
}
|
||||
|
||||
int signatures_ok(const image_header *hdr, uint8_t store_fingerprint[32],
|
||||
bool use_verifymessage) {
|
||||
secbool use_verifymessage) {
|
||||
uint8_t hash[32] = {0};
|
||||
// which set of public keys depend on scheme
|
||||
const uint8_t *const *pubkey_ptr = NULL;
|
||||
if (use_verifymessage) {
|
||||
uint8_t pubkeys = 0;
|
||||
if (use_verifymessage == sectrue) {
|
||||
pubkey_ptr = pubkey_v3;
|
||||
compute_firmware_fingerprint_for_verifymessage(hdr, hash);
|
||||
pubkeys = PUBKEYS_V3;
|
||||
} else {
|
||||
pubkey_ptr = pubkey_v2;
|
||||
compute_firmware_fingerprint(hdr, hash);
|
||||
pubkeys = PUBKEYS_V2;
|
||||
}
|
||||
|
||||
if (store_fingerprint) {
|
||||
memcpy(store_fingerprint, hash, 32);
|
||||
}
|
||||
|
||||
if (hdr->sigindex1 < 1 || hdr->sigindex1 > PUBKEYS)
|
||||
if (hdr->sigindex1 < 1 || hdr->sigindex1 > pubkeys)
|
||||
return SIG_FAIL; // invalid index
|
||||
if (hdr->sigindex2 < 1 || hdr->sigindex2 > PUBKEYS)
|
||||
return SIG_FAIL; // invalid index
|
||||
if (hdr->sigindex3 < 1 || hdr->sigindex3 > PUBKEYS)
|
||||
if (hdr->sigindex2 < 1 || hdr->sigindex2 > pubkeys)
|
||||
return SIG_FAIL; // invalid index
|
||||
if (use_verifymessage != sectrue) {
|
||||
if (hdr->sigindex3 < 1 || hdr->sigindex3 > pubkeys) {
|
||||
return SIG_FAIL; // invalid index
|
||||
}
|
||||
} else if (hdr->sigindex3 != 0) {
|
||||
return SIG_FAIL;
|
||||
}
|
||||
|
||||
if (hdr->sigindex1 == hdr->sigindex2) return SIG_FAIL; // duplicate use
|
||||
if (hdr->sigindex1 == hdr->sigindex3) return SIG_FAIL; // duplicate use
|
||||
@ -206,9 +211,18 @@ int signatures_ok(const image_header *hdr, uint8_t store_fingerprint[32],
|
||||
hdr->sig2, hash)) { // failure
|
||||
return SIG_FAIL;
|
||||
}
|
||||
if (0 != ecdsa_verify_digest(&secp256k1, pubkey_ptr[hdr->sigindex3 - 1],
|
||||
hdr->sig3, hash)) { // failure
|
||||
return SIG_FAIL;
|
||||
if (use_verifymessage != sectrue) {
|
||||
if (0 != ecdsa_verify_digest(&secp256k1, pubkey_ptr[hdr->sigindex3 - 1],
|
||||
hdr->sig3, hash)) // failure
|
||||
{
|
||||
return SIG_FAIL;
|
||||
}
|
||||
} else {
|
||||
for (unsigned int i = 0; i < sizeof(hdr->sig3); i++) {
|
||||
if (hdr->sig3[i] != 0) {
|
||||
return SIG_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SIG_OK;
|
||||
@ -221,8 +235,8 @@ int signatures_match(const image_header *hdr, uint8_t store_fingerprint[32]) {
|
||||
// timing side channels.
|
||||
// Return only the hash for the v2 computation so that it is
|
||||
// the same shown in previous bootloader.
|
||||
result ^= signatures_ok(hdr, store_fingerprint, false);
|
||||
result ^= signatures_ok(hdr, NULL, true);
|
||||
result ^= signatures_ok(hdr, store_fingerprint, secfalse);
|
||||
result ^= signatures_ok(hdr, NULL, sectrue);
|
||||
if (result != SIG_OK) {
|
||||
return SIG_FAIL;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "secbool.h"
|
||||
|
||||
extern const uint32_t FIRMWARE_MAGIC_NEW; // TRZF
|
||||
|
||||
@ -43,7 +44,10 @@ typedef struct {
|
||||
uint32_t codelen;
|
||||
uint32_t version;
|
||||
uint32_t fix_version;
|
||||
uint8_t __reserved1[8];
|
||||
uint32_t hw_model;
|
||||
uint8_t hw_revision;
|
||||
uint8_t monotonic;
|
||||
uint8_t __reserved1[2];
|
||||
uint8_t hashes[512];
|
||||
uint8_t sig1[64];
|
||||
uint8_t sig2[64];
|
||||
@ -105,7 +109,7 @@ void compute_firmware_fingerprint_for_verifymessage(const image_header *hdr,
|
||||
* @return SIG_OK or SIG_FAIL
|
||||
*/
|
||||
int signatures_ok(const image_header *hdr, uint8_t store_fingerprint[32],
|
||||
bool use_verifymessage);
|
||||
secbool use_verifymessage);
|
||||
|
||||
/**
|
||||
* Check if either v2 or v3 signature of header is valid.
|
||||
|
1
legacy/intermediate_fw/.changelog.d/163.fixed
Normal file
1
legacy/intermediate_fw/.changelog.d/163.fixed
Normal file
@ -0,0 +1 @@
|
||||
Bootloader VTOR and FW handover fix
|
@ -5,6 +5,7 @@ NAME = trezor
|
||||
OBJS += trezor.o
|
||||
OBJS += header.o
|
||||
OBJS += bl_check.o
|
||||
OBJS += startup.o
|
||||
OBJS += ../vendor/trezor-crypto/memzero.o
|
||||
OBJS += ../vendor/trezor-crypto/sha2.o
|
||||
|
||||
@ -43,10 +44,15 @@ endif
|
||||
@printf " MAKO $@\n"
|
||||
$(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
|
||||
@printf " PYTHON bl_data.py\n"
|
||||
$(Q)$(PYTHON) bl_data.py
|
||||
|
||||
@printf " PYTHON bl_data.py bootloader.dat\n"
|
||||
$(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::
|
||||
rm -f bl_data.h
|
||||
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
|
1
legacy/intermediate_fw/startup.S
Symbolic link
1
legacy/intermediate_fw/startup.S
Symbolic link
@ -0,0 +1 @@
|
||||
../firmware/startup.S
|
@ -1,8 +1,8 @@
|
||||
// Matches the bootloader version included in this firmware
|
||||
#define VERSION_MAJOR 1
|
||||
#define VERSION_MINOR 10
|
||||
#define VERSION_PATCH 0
|
||||
#define VERSION_MINOR 12
|
||||
#define VERSION_PATCH 1
|
||||
|
||||
#define FIX_VERSION_MAJOR 1
|
||||
#define FIX_VERSION_MINOR 10
|
||||
#define FIX_VERSION_MINOR 12
|
||||
#define FIX_VERSION_PATCH 0
|
||||
|
@ -30,6 +30,9 @@
|
||||
|
||||
void memory_protect(void) {
|
||||
#if PRODUCTION
|
||||
#if BOOTLOADER_QA
|
||||
#error BOOTLOADER_QA must be built with PRODUCTION=0
|
||||
#endif
|
||||
// Reference STM32F205 Flash programming manual revision 5
|
||||
// http://www.st.com/resource/en/programming_manual/cd00233952.pdf Section 2.6
|
||||
// Option bytes
|
||||
@ -73,11 +76,16 @@ void memory_protect(void) {
|
||||
// example in STM32F427, where the protection bits are read correctly
|
||||
// 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) {
|
||||
#if PRODUCTION
|
||||
#if BOOTLOADER_QA
|
||||
#error BOOTLOADER_QA must be built with PRODUCTION=0
|
||||
#endif
|
||||
flash_unlock_option_bytes();
|
||||
flash_program_option_bytes(0x0FFFCCEC);
|
||||
flash_lock_option_bytes();
|
||||
#endif
|
||||
}
|
||||
|
||||
int memory_bootloader_hash(uint8_t *hash) {
|
||||
|
166
legacy/script/update_bootloader.py
Executable file
166
legacy/script/update_bootloader.py
Executable file
@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import typing as t
|
||||
from hashlib import sha256
|
||||
from pathlib import Path
|
||||
|
||||
import click
|
||||
|
||||
LEGACY_ROOT = Path(__file__).parent.parent.resolve()
|
||||
|
||||
BOOTLOADER_BUILT = LEGACY_ROOT / "bootloader" / "bootloader.bin"
|
||||
BOOTLOADER_IMAGE = LEGACY_ROOT / "firmware" / "bootloader.dat"
|
||||
BOOTLOADER_QA_IMAGE = LEGACY_ROOT / "firmware" / "bootloader_qa.dat"
|
||||
|
||||
BOOTLOADER_VERSION = LEGACY_ROOT / "bootloader" / "version.h"
|
||||
FIRMWARE_VERSION = LEGACY_ROOT / "firmware" / "version.h"
|
||||
|
||||
BL_CHECK_C = LEGACY_ROOT / "firmware" / "bl_check.c"
|
||||
BL_CHECK_TXT = LEGACY_ROOT / "firmware" / "bl_check.txt"
|
||||
BL_CHECK_QA_TXT = LEGACY_ROOT / "firmware" / "bl_check_qa.txt"
|
||||
|
||||
BL_CHECK_PATTERN = """\
|
||||
if (0 ==
|
||||
memcmp(hash,
|
||||
{line1}
|
||||
{line2},
|
||||
32))
|
||||
return 1; // {comment}
|
||||
"""
|
||||
|
||||
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_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:
|
||||
"""Convert bytes to C string literal.
|
||||
|
||||
>>> cstrify(b"foo")
|
||||
'"\\x66\\x6f\\x6f"'
|
||||
"""
|
||||
return '"' + "".join(rf"\x{b:02x}" for b in data) + '"'
|
||||
|
||||
|
||||
def load_version(filename: Path) -> str:
|
||||
"""Load version from version.h"""
|
||||
vdict = {}
|
||||
with open(filename) as f:
|
||||
for line in f:
|
||||
if line.startswith("#define VERSION_"):
|
||||
_define, symbol, value = line.split()
|
||||
_, name = symbol.lower().split("_", maxsplit=1)
|
||||
vdict[name] = int(value)
|
||||
|
||||
return "{major}.{minor}.{patch}".format(**vdict)
|
||||
|
||||
|
||||
def load_hash_entries(txt_file) -> dict[bytes, str]:
|
||||
"""Load hash entries from bl_check.txt"""
|
||||
return {
|
||||
bytes.fromhex(digest): comment
|
||||
for digest, comment in (
|
||||
line.split(" ", maxsplit=1) for line in txt_file.read_text().splitlines()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def regenerate_bl_check(
|
||||
hash_entries: t.Iterable[tuple[bytes, str]],
|
||||
begin,
|
||||
end,
|
||||
) -> None:
|
||||
"""Regenerate bl_check.c with given hash entries."""
|
||||
bl_check_new = []
|
||||
with open(BL_CHECK_C) as f:
|
||||
# read up to AUTO-BEGIN
|
||||
for line in f:
|
||||
bl_check_new.append(line)
|
||||
if line == begin:
|
||||
break
|
||||
|
||||
# generate new sections
|
||||
for digest, comment in hash_entries:
|
||||
bl_check_new.append(
|
||||
BL_CHECK_PATTERN.format(
|
||||
line1=cstrify(digest[:16]),
|
||||
line2=cstrify(digest[16:]),
|
||||
comment=comment,
|
||||
)
|
||||
)
|
||||
|
||||
# consume up to AUTO-END
|
||||
for line in f:
|
||||
if line == end:
|
||||
bl_check_new.append(line)
|
||||
break
|
||||
|
||||
# add rest of the file contents
|
||||
bl_check_new.extend(f)
|
||||
|
||||
BL_CHECK_C.write_text("".join(bl_check_new))
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("-c", "--comment", help="Comment for the hash entry.")
|
||||
@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.
|
||||
|
||||
Takes bootloader/boootloader.dat, copies over firmware/bootloader.dat, and adds
|
||||
an entry to firmware/bl_check.txt and bl_check.c
|
||||
"""
|
||||
bl_bytes = BOOTLOADER_BUILT.read_bytes()
|
||||
digest = sha256(sha256(bl_bytes).digest()).digest()
|
||||
click.echo("Bootloader digest: " + digest.hex())
|
||||
|
||||
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:
|
||||
click.echo("Bootloader already in bl_check.txt: " + entries[digest])
|
||||
|
||||
else:
|
||||
if comment is None:
|
||||
bl_version = load_version(BOOTLOADER_VERSION)
|
||||
fw_version = load_version(FIRMWARE_VERSION)
|
||||
comment = f"{bl_version} shipped with fw {fw_version}"
|
||||
|
||||
# insert new bootloader
|
||||
with open(txt_file, "a") as f:
|
||||
f.write(f"{digest.hex()} {comment}\n")
|
||||
|
||||
entries[digest] = comment
|
||||
click.echo("Inserted new entry: " + comment)
|
||||
|
||||
# rewrite bl_check.c
|
||||
regenerate_bl_check(entries.items(), begin, end)
|
||||
click.echo("Regenerated bl_check.c")
|
||||
|
||||
# overwrite bootloader.dat
|
||||
image.write_bytes(bl_bytes)
|
||||
|
||||
if qa:
|
||||
click.echo("Installed bootloader_qa.dat into firmware")
|
||||
else:
|
||||
click.echo("Installed bootloader.dat into firmware")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -221,7 +221,7 @@ void mpu_config_bootloader(void) {
|
||||
|
||||
// Never use in bootloader! Disables access to PPB (including MPU, NVIC, SCB)
|
||||
void mpu_config_firmware(void) {
|
||||
#if PRODUCTION
|
||||
#if PRODUCTION || BOOTLOADER_QA
|
||||
// Disable MPU
|
||||
MPU_CTRL = 0;
|
||||
|
||||
|
@ -80,7 +80,11 @@ jump_to_firmware(const vector_table_t *ivt, int trust) {
|
||||
} else { // untrusted firmware
|
||||
timer_init();
|
||||
mpu_config_firmware(); // * configure MPU for the firmware
|
||||
__asm__ volatile("msr msp, %0" ::"r"(_stack));
|
||||
|
||||
// Setup stack in unprivileged mode (MSR works only for privileged)
|
||||
// This syntax will use _stack as immediate value to put into SP
|
||||
// instead of dereferencing it
|
||||
__asm__ volatile("mov sp, %[input]" ::[input] "r"(&_stack));
|
||||
}
|
||||
|
||||
// Jump to address
|
||||
|
@ -88,9 +88,6 @@ def cli(project, version):
|
||||
VERSION_MAJOR=major,
|
||||
VERSION_MINOR=minor,
|
||||
VERSION_PATCH=patch,
|
||||
VERSION_MAJOR_CHAR=hex_lit(major),
|
||||
VERSION_MINOR_CHAR=hex_lit(minor),
|
||||
VERSION_PATCH_CHAR=hex_lit(patch),
|
||||
)
|
||||
else:
|
||||
raise click.ClickException(f"Unknown project {project}.")
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
## [{{ versiondata.version }}] ({{versiondata.date}})
|
||||
## {{ versiondata.version }} [{{versiondata.date}}]
|
||||
{% for section, _ in sections.items() %}
|
||||
{% if section %}{{section}}{% endif -%}
|
||||
{% if sections[section] %}
|
||||
|
Loading…
Reference in New Issue
Block a user