diff --git a/build-docker.sh b/build-docker.sh index 59f77cc47..4ab13fe53 100755 --- a/build-docker.sh +++ b/build-docker.sh @@ -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 +} + +OPT_BUILD_CORE=1 +OPT_BUILD_LEGACY=1 +OPT_BUILD_NORMAL=1 +OPT_BUILD_BITCOINONLY=1 + +REPOSITORY="/local" -if [ "$1" == "--skip-bitcoinonly" ]; then - VARIANTS_core=(0) - VARIANTS_legacy=(0) - shift +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} diff --git a/core/Makefile b/core/Makefile index d93063de7..c38351fc1 100644 --- a/core/Makefile +++ b/core/Makefile @@ -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)" diff --git a/core/SConscript.firmware b/core/SConscript.firmware index 0226fc18d..c3f677acd 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -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( diff --git a/core/embed/firmware/bootloaders/bootloader_1.bin b/core/embed/firmware/bootloaders/bootloader_1.bin new file mode 120000 index 000000000..6a019c96b --- /dev/null +++ b/core/embed/firmware/bootloaders/bootloader_1.bin @@ -0,0 +1 @@ +../../../../legacy/firmware/bootloader.dat \ No newline at end of file diff --git a/docs/legacy/index.md b/docs/legacy/index.md index 344b992e2..48ae4c0e4 100644 --- a/docs/legacy/index.md +++ b/docs/legacy/index.md @@ -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. diff --git a/legacy/Makefile b/legacy/Makefile index 2f6f9eb50..5fb84a7ff 100644 --- a/legacy/Makefile +++ b/legacy/Makefile @@ -1,6 +1,5 @@ ifneq ($(EMULATOR),1) OBJS += setup.o -OBJS += startup.o OBJS += timer.o endif diff --git a/legacy/Makefile.include b/legacy/Makefile.include index f7cbd6ec2..261316957 100644 --- a/legacy/Makefile.include +++ b/legacy/Makefile.include @@ -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 diff --git a/legacy/bootloader/.changelog.d/2568.added b/legacy/bootloader/.changelog.d/2568.added deleted file mode 100644 index 77b0e992f..000000000 --- a/legacy/bootloader/.changelog.d/2568.added +++ /dev/null @@ -1 +0,0 @@ -T1 bootloader: verify firmware signatures based on SignMessage, add signature debugging diff --git a/legacy/bootloader/CHANGELOG.md b/legacy/bootloader/CHANGELOG.md index ca0507070..8067ffb86 100644 --- a/legacy/bootloader/CHANGELOG.md +++ b/legacy/bootloader/CHANGELOG.md @@ -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 diff --git a/legacy/bootloader/Makefile b/legacy/bootloader/Makefile index dd8b0b866..7df46de99 100644 --- a/legacy/bootloader/Makefile +++ b/legacy/bootloader/Makefile @@ -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 diff --git a/legacy/bootloader/bootloader.c b/legacy/bootloader/bootloader.c index 31c046f45..af78230e9 100644 --- a/legacy/bootloader/bootloader.c +++ b/legacy/bootloader/bootloader.c @@ -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."); diff --git a/legacy/bootloader/firmware_sign_dev.py b/legacy/bootloader/firmware_sign_dev.py new file mode 100644 index 000000000..af66ce1ee --- /dev/null +++ b/legacy/bootloader/firmware_sign_dev.py @@ -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(" 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("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; } diff --git a/legacy/bootloader/version.h b/legacy/bootloader/version.h index c0c3d1f88..3d7066741 100644 --- a/legacy/bootloader/version.h +++ b/legacy/bootloader/version.h @@ -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 diff --git a/legacy/debug_signing/README.md b/legacy/debug_signing/README.md index 7b4044e70..d8ad3f837 100644 --- a/legacy/debug_signing/README.md +++ b/legacy/debug_signing/README.md @@ -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`. diff --git a/legacy/debug_signing/fill_t1_fw_signatures.py b/legacy/debug_signing/fill_t1_fw_signatures.py index 97f1ceec7..e3629d723 100755 --- a/legacy/debug_signing/fill_t1_fw_signatures.py +++ b/legacy/debug_signing/fill_t1_fw_signatures.py @@ -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] diff --git a/legacy/debug_signing/firmware_hash_sign_trezor.py b/legacy/debug_signing/firmware_hash_sign_trezor.py index b662565a1..c11893da4 100755 --- a/legacy/debug_signing/firmware_hash_sign_trezor.py +++ b/legacy/debug_signing/firmware_hash_sign_trezor.py @@ -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}") diff --git a/legacy/debug_signing/sign_firmware_v2_signature.py b/legacy/debug_signing/sign_firmware_v2_signature.py index 686ebfe96..6abecfc2b 100755 --- a/legacy/debug_signing/sign_firmware_v2_signature.py +++ b/legacy/debug_signing/sign_firmware_v2_signature.py @@ -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", ] diff --git a/legacy/debug_signing/sign_firmware_v3_signature.py b/legacy/debug_signing/sign_firmware_v3_signature.py new file mode 100644 index 000000000..e6a0ce4a6 --- /dev/null +++ b/legacy/debug_signing/sign_firmware_v3_signature.py @@ -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() diff --git a/legacy/demo/Makefile b/legacy/demo/Makefile index eba48a4f2..6fd3f0985 100644 --- a/legacy/demo/Makefile +++ b/legacy/demo/Makefile @@ -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 diff --git a/legacy/demo/startup.S b/legacy/demo/startup.S new file mode 120000 index 000000000..efb899b49 --- /dev/null +++ b/legacy/demo/startup.S @@ -0,0 +1 @@ +../bootloader/startup.S \ No newline at end of file diff --git a/legacy/firmware/.changelog.d/1453.fixed b/legacy/firmware/.changelog.d/1453.fixed deleted file mode 100644 index 1672b03f0..000000000 --- a/legacy/firmware/.changelog.d/1453.fixed +++ /dev/null @@ -1 +0,0 @@ -Show full Stellar address and QR code. diff --git a/legacy/firmware/.changelog.d/1749.added b/legacy/firmware/.changelog.d/1749.added deleted file mode 100644 index 26482972a..000000000 --- a/legacy/firmware/.changelog.d/1749.added +++ /dev/null @@ -1 +0,0 @@ -Support Ledger Live legacy derivation path "m/44'/coin_type'/0'/account" diff --git a/legacy/firmware/.changelog.d/2190.changed b/legacy/firmware/.changelog.d/2190.changed deleted file mode 100644 index 4872888c6..000000000 --- a/legacy/firmware/.changelog.d/2190.changed +++ /dev/null @@ -1 +0,0 @@ -Do not convert bech32 addresses to uppercase in QR code to increase compatibility diff --git a/legacy/firmware/.changelog.d/2289.changed b/legacy/firmware/.changelog.d/2289.changed deleted file mode 100644 index 245836460..000000000 --- a/legacy/firmware/.changelog.d/2289.changed +++ /dev/null @@ -1 +0,0 @@ -Do not allow access to SLIP25 paths. diff --git a/legacy/firmware/.changelog.d/2373.fixed b/legacy/firmware/.changelog.d/2373.fixed deleted file mode 100644 index 631922d94..000000000 --- a/legacy/firmware/.changelog.d/2373.fixed +++ /dev/null @@ -1 +0,0 @@ -Wrap long Ethereum fee to next line if it does not fit. diff --git a/legacy/firmware/.changelog.d/2442.added b/legacy/firmware/.changelog.d/2442.added deleted file mode 100644 index c9b38f712..000000000 --- a/legacy/firmware/.changelog.d/2442.added +++ /dev/null @@ -1 +0,0 @@ -Show fee rate when replacing transaction diff --git a/legacy/firmware/.changelog.d/2486.changed b/legacy/firmware/.changelog.d/2486.changed deleted file mode 100644 index 41fe5c3fa..000000000 --- a/legacy/firmware/.changelog.d/2486.changed +++ /dev/null @@ -1 +0,0 @@ -Extend decimals of fee rate to 2 digits diff --git a/legacy/firmware/.changelog.d/2487.changed b/legacy/firmware/.changelog.d/2487.changed deleted file mode 100644 index 254933514..000000000 --- a/legacy/firmware/.changelog.d/2487.changed +++ /dev/null @@ -1 +0,0 @@ -Display only sat instead of sat BTC diff --git a/legacy/firmware/.changelog.d/2568.added b/legacy/firmware/.changelog.d/2568.added deleted file mode 100644 index 77b0e992f..000000000 --- a/legacy/firmware/.changelog.d/2568.added +++ /dev/null @@ -1 +0,0 @@ -T1 bootloader: verify firmware signatures based on SignMessage, add signature debugging diff --git a/legacy/firmware/.changelog.d/2682.added b/legacy/firmware/.changelog.d/2682.added deleted file mode 100644 index 265e0d1f3..000000000 --- a/legacy/firmware/.changelog.d/2682.added +++ /dev/null @@ -1 +0,0 @@ -Allow proposed Casa m/45' multisig paths for Bitcoin and Ethereum. diff --git a/legacy/firmware/.changelog.d/2718.added b/legacy/firmware/.changelog.d/2718.added deleted file mode 100644 index ba5e0de52..000000000 --- a/legacy/firmware/.changelog.d/2718.added +++ /dev/null @@ -1 +0,0 @@ -Support native SegWit external inputs with non-ownership proof. diff --git a/legacy/firmware/.changelog.d/2718.added.1 b/legacy/firmware/.changelog.d/2718.added.1 deleted file mode 100644 index 2cbac75c0..000000000 --- a/legacy/firmware/.changelog.d/2718.added.1 +++ /dev/null @@ -1 +0,0 @@ -Implement SLIP-0019 proofs of ownership for native SegWit. diff --git a/legacy/firmware/.changelog.d/2718.added.2 b/legacy/firmware/.changelog.d/2718.added.2 deleted file mode 100644 index a7372ecde..000000000 --- a/legacy/firmware/.changelog.d/2718.added.2 +++ /dev/null @@ -1 +0,0 @@ -Implement `serialize` option in SignTx. diff --git a/legacy/firmware/.changelog.d/2718.added.3 b/legacy/firmware/.changelog.d/2718.added.3 deleted file mode 100644 index 38d0a16cc..000000000 --- a/legacy/firmware/.changelog.d/2718.added.3 +++ /dev/null @@ -1 +0,0 @@ -Implement SLIP-0025 coinjoin accounts. diff --git a/legacy/firmware/.changelog.d/2718.added.4 b/legacy/firmware/.changelog.d/2718.added.4 deleted file mode 100644 index 6146c8b2e..000000000 --- a/legacy/firmware/.changelog.d/2718.added.4 +++ /dev/null @@ -1 +0,0 @@ -Implement coinjoin signing. diff --git a/legacy/firmware/.changelog.d/2743.changed b/legacy/firmware/.changelog.d/2743.changed deleted file mode 100644 index 88dc53d70..000000000 --- a/legacy/firmware/.changelog.d/2743.changed +++ /dev/null @@ -1 +0,0 @@ -Increase 'SignIdentity.challenge_hidden' max_size to 512 bytes diff --git a/legacy/firmware/.changelog.d/noissue.security b/legacy/firmware/.changelog.d/noissue.security deleted file mode 100644 index 7318011cc..000000000 --- a/legacy/firmware/.changelog.d/noissue.security +++ /dev/null @@ -1 +0,0 @@ -Match and validate script type of change-outputs in Bitcoin signing. diff --git a/legacy/firmware/CHANGELOG.md b/legacy/firmware/CHANGELOG.md index 97247e77a..0e0439721 100644 --- a/legacy/firmware/CHANGELOG.md +++ b/legacy/firmware/CHANGELOG.md @@ -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 diff --git a/legacy/firmware/Makefile b/legacy/firmware/Makefile index 75a46b6ec..755b970d5 100644 --- a/legacy/firmware/Makefile +++ b/legacy/firmware/Makefile @@ -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 diff --git a/legacy/firmware/bl_check.c b/legacy/firmware/bl_check.c index eeb5eb225..47a034bee 100644 --- a/legacy/firmware/bl_check.c +++ b/legacy/firmware/bl_check.c @@ -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); diff --git a/legacy/firmware/bl_check.txt b/legacy/firmware/bl_check.txt new file mode 100644 index 000000000..a2f8376eb --- /dev/null +++ b/legacy/firmware/bl_check.txt @@ -0,0 +1,2 @@ +e9ec8fa2fefad2b3b6b7c4ab76691a3361b3eefb8a0ddaa34d7e45b63b3d5664 1.12.0 shipped with fw 1.12.0 +94f1c90db28db1f8ce5dca966976343658f5dadee83834987c8b049c49d1edd0 1.12.1 shipped with fw 1.12.1 diff --git a/legacy/firmware/bl_check_qa.txt b/legacy/firmware/bl_check_qa.txt new file mode 100644 index 000000000..994e3def5 --- /dev/null +++ b/legacy/firmware/bl_check_qa.txt @@ -0,0 +1,2 @@ +21b949f4f5fd9f3a7d6343d1076f960fb45418196531b9f2974a68ede8db2ea1 1.12.0 shipped with fw 1.12.0 +b4e59244185ce1cd6c8f590310370218509c393226f0074b8cf6adedb3cd4d55 1.12.1 shipped with fw 1.12.1 diff --git a/legacy/firmware/bl_data.py b/legacy/firmware/bl_data.py index 6d0207f2b..6a12d614c 100755 --- a/legacy/firmware/bl_data.py +++ b/legacy/firmware/bl_data.py @@ -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") diff --git a/legacy/firmware/bootloader.dat b/legacy/firmware/bootloader.dat index ac9d56f23..cd44dc746 100644 Binary files a/legacy/firmware/bootloader.dat and b/legacy/firmware/bootloader.dat differ diff --git a/legacy/firmware/bootloader_qa.dat b/legacy/firmware/bootloader_qa.dat new file mode 100644 index 000000000..e7bc68ade Binary files /dev/null and b/legacy/firmware/bootloader_qa.dat differ diff --git a/legacy/firmware/fsm_msg_common.h b/legacy/firmware/fsm_msg_common.h index dca921f54..5debd36ea 100644 --- a/legacy/firmware/fsm_msg_common.h +++ b/legacy/firmware/fsm_msg_common.h @@ -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)); diff --git a/legacy/firmware/header.S b/legacy/firmware/header.S index 46e4aa375..87a7dba30 100644 --- a/legacy/firmware/header.S +++ b/legacy/firmware/header.S @@ -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 diff --git a/legacy/firmware/startup.S b/legacy/firmware/startup.S new file mode 100644 index 000000000..81264a6ac --- /dev/null +++ b/legacy/firmware/startup.S @@ -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 diff --git a/legacy/firmware/version.h b/legacy/firmware/version.h index d795886c3..77eccd0f5 100644 --- a/legacy/firmware/version.h +++ b/legacy/firmware/version.h @@ -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 diff --git a/legacy/fw_signatures.c b/legacy/fw_signatures.c index 4317f8b9b..92e0f85cc 100644 --- a/legacy/fw_signatures.c +++ b/legacy/fw_signatures.c @@ -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) - return SIG_FAIL; // invalid index - if (hdr->sigindex2 < 1 || hdr->sigindex2 > PUBKEYS) + if (hdr->sigindex1 < 1 || hdr->sigindex1 > 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; } diff --git a/legacy/fw_signatures.h b/legacy/fw_signatures.h index ab2a450bf..e9c76f363 100644 --- a/legacy/fw_signatures.h +++ b/legacy/fw_signatures.h @@ -22,6 +22,7 @@ #include #include +#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. diff --git a/legacy/intermediate_fw/.changelog.d/163.fixed b/legacy/intermediate_fw/.changelog.d/163.fixed new file mode 100644 index 000000000..c21b51ff2 --- /dev/null +++ b/legacy/intermediate_fw/.changelog.d/163.fixed @@ -0,0 +1 @@ +Bootloader VTOR and FW handover fix diff --git a/legacy/intermediate_fw/Makefile b/legacy/intermediate_fw/Makefile index 2e94591a0..166ddbfb9 100644 --- a/legacy/intermediate_fw/Makefile +++ b/legacy/intermediate_fw/Makefile @@ -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 diff --git a/legacy/intermediate_fw/bootloader_qa.dat b/legacy/intermediate_fw/bootloader_qa.dat new file mode 120000 index 000000000..b50597d6f --- /dev/null +++ b/legacy/intermediate_fw/bootloader_qa.dat @@ -0,0 +1 @@ +../firmware/bootloader_qa.dat \ No newline at end of file diff --git a/legacy/intermediate_fw/startup.S b/legacy/intermediate_fw/startup.S new file mode 120000 index 000000000..379dbac45 --- /dev/null +++ b/legacy/intermediate_fw/startup.S @@ -0,0 +1 @@ +../firmware/startup.S \ No newline at end of file diff --git a/legacy/intermediate_fw/version.h b/legacy/intermediate_fw/version.h index 7c7293249..7bf83f3c7 100644 --- a/legacy/intermediate_fw/version.h +++ b/legacy/intermediate_fw/version.h @@ -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 diff --git a/legacy/memory.c b/legacy/memory.c index 39cebb3f9..9f66b94ca 100644 --- a/legacy/memory.c +++ b/legacy/memory.c @@ -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) { diff --git a/legacy/script/update_bootloader.py b/legacy/script/update_bootloader.py new file mode 100755 index 000000000..14b281430 --- /dev/null +++ b/legacy/script/update_bootloader.py @@ -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() diff --git a/legacy/setup.c b/legacy/setup.c index 21ad3c37d..1537c3fb1 100644 --- a/legacy/setup.c +++ b/legacy/setup.c @@ -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; diff --git a/legacy/util.h b/legacy/util.h index 9ecb05936..d0b31c7ef 100644 --- a/legacy/util.h +++ b/legacy/util.h @@ -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 diff --git a/tools/bump-version.py b/tools/bump-version.py index cfd974a74..230ff02b9 100755 --- a/tools/bump-version.py +++ b/tools/bump-version.py @@ -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}.") diff --git a/tools/towncrier.template.md b/tools/towncrier.template.md index 2a5c58a02..834012add 100644 --- a/tools/towncrier.template.md +++ b/tools/towncrier.template.md @@ -1,5 +1,5 @@ -## [{{ versiondata.version }}] ({{versiondata.date}}) +## {{ versiondata.version }} [{{versiondata.date}}] {% for section, _ in sections.items() %} {% if section %}{{section}}{% endif -%} {% if sections[section] %}