diff --git a/core/SConscript.secmon b/core/SConscript.secmon index 69b1744afa..c8a526771b 100644 --- a/core/SConscript.secmon +++ b/core/SConscript.secmon @@ -271,6 +271,11 @@ SOURCE_FIRMWARE = [ 'embed/projects/secmon/main.c', ] +if TREZOR_MODEL not in ('T3B1', 'T3T1'): + SOURCE_FIRMWARE += [ + 'embed/projects/secmon/header.S', + ] + if 'sd_card' in FEATURES_AVAILABLE: SDCARD = True else: @@ -411,10 +416,17 @@ BINARY_NAME += "-" + tools.get_git_revision_short_hash() BINARY_NAME += "-dirty" if tools.get_git_modified() else "" BINARY_NAME += ".bin" -action_bin=[ - '$OBJCOPY -O binary -j .flash -j .data -j .confidential -j .gnu.sgstubs $SOURCE ${TARGET}', - '$CP $TARGET ' + BINARY_NAME, -] +if TREZOR_MODEL in ('T3B1', 'T3T1'): + action_bin=[ + '$OBJCOPY -O binary -j .secmon_header -j .flash -j .data -j .confidential -j .gnu.sgstubs $SOURCE ${TARGET}', + '$CP $TARGET ' + BINARY_NAME, + ] +else: + action_bin=[ + '$OBJCOPY -O binary -j .secmon_header -j .flash -j .data -j .confidential -j .gnu.sgstubs $SOURCE ${TARGET}', + '$HEADERTOOL -h $TARGET ' + ('-D' if not PRODUCTION else ''), + '$CP $TARGET ' + BINARY_NAME, + ] if STORAGE_INSECURE_TESTING_MODE: INSECURE_TESTING_MODE_STR = """ diff --git a/core/embed/projects/secmon/header.S b/core/embed/projects/secmon/header.S new file mode 100644 index 0000000000..9b2c920d96 --- /dev/null +++ b/core/embed/projects/secmon/header.S @@ -0,0 +1,28 @@ + .syntax unified + +#include "version.h" + + .section .secmon_header, "a" + + .type g_header, %object + .size g_header, .-g_header + +// Secure monitor header + +g_header: + .byte 'T','S','E','C' // magic + .word g_header_end - g_header // hdrlen + .word _codelen // codelen + .byte VERSION_MAJOR // vmajor + .byte VERSION_MINOR // vminor + .byte VERSION_PATCH // vpatch + .byte VERSION_BUILD // vbuild + .word HW_MODEL // type of the designated hardware + .byte HW_REVISION // revision of the designated hardware + . = . + 3 // reserved + . = . + 32 // hash of entire secmon + . = . + 391 // reserved + .byte 0 // sigmask + . = . + 64 // sig +g_header_end: + diff --git a/core/embed/projects/secmon/main.c b/core/embed/projects/secmon/main.c index 08591e89f9..bbfe79cbd6 100644 --- a/core/embed/projects/secmon/main.c +++ b/core/embed/projects/secmon/main.c @@ -107,8 +107,8 @@ static void secmon_panic(const systask_postmortem_t *pminfo) { } // defined in linker script -extern uint32_t _codelen; -#define SECMON_SIZE ((uint32_t) & _codelen) +extern uint32_t _secmon_size; +#define SECMON_SIZE ((uint32_t) & _secmon_size) #define KERNEL_START (FIRMWARE_START + SECMON_SIZE) int main(void) { diff --git a/core/embed/sys/linker/stm32u5g/secmon.ld b/core/embed/sys/linker/stm32u5g/secmon.ld index fc2113d191..ff1859440c 100644 --- a/core/embed/sys/linker/stm32u5g/secmon.ld +++ b/core/embed/sys/linker/stm32u5g/secmon.ld @@ -28,7 +28,8 @@ _confidential_section_end = ADDR(.confidential) + SIZEOF(.confidential); _bootargs_ram_start = BOOTARGS_START; _bootargs_ram_end = BOOTARGS_START + BOOTARGS_SIZE; -_codelen = _secmon_flash_end - ORIGIN(FLASH); +_codelen = _secmon_flash_end - _secmon_code_start; +_secmon_size = _secmon_flash_end - ORIGIN(FLASH); SECTIONS { .vendorheader : ALIGN(4) { @@ -40,6 +41,15 @@ SECTIONS { . = ALIGN(CODE_ALIGNMENT); } >FLASH + .secmon_header : ALIGN(4) { + KEEP(*(.secmon_header)); + } >FLASH + + .padding : ALIGN(4) { + _secmon_code_start = .; + . = ALIGN(CODE_ALIGNMENT); + } >FLASH AT>FLASH + .flash : ALIGN(CODE_ALIGNMENT) { KEEP(*(.vector_table)); . = ALIGN(4); diff --git a/core/embed/sys/trustzone/stm32u5/trustzone.c b/core/embed/sys/trustzone/stm32u5/trustzone.c index 631e317a89..57d6cc9c98 100644 --- a/core/embed/sys/trustzone/stm32u5/trustzone.c +++ b/core/embed/sys/trustzone/stm32u5/trustzone.c @@ -61,9 +61,9 @@ extern uint8_t _sgstubs_section_end; #define SGSTUBS_SIZE (SGSTUBS_END - SGSTUBS_START) // defined in linker script -extern uint32_t _codelen; +extern uint32_t _secmon_size; -#define SECMON_SIZE ((uint32_t) & _codelen) +#define SECMON_SIZE ((uint32_t) & _secmon_size) #define NONSECURE_CODE_START (FIRMWARE_START + SECMON_SIZE) #define NONSECURE_CODE_SIZE (FIRMWARE_MAXSIZE - SECMON_SIZE) diff --git a/core/tools/trezor_core_tools/headertool.py b/core/tools/trezor_core_tools/headertool.py index 1302b40f05..049acad771 100755 --- a/core/tools/trezor_core_tools/headertool.py +++ b/core/tools/trezor_core_tools/headertool.py @@ -28,6 +28,8 @@ def do_rehash(fw: firmware_headers.SignableImageProto) -> None: """Recalculate the code hashes inside the header.""" if isinstance(fw, firmware.FirmwareImage): fw.header.hashes = fw.code_hashes() + if isinstance(fw, firmware.SecmonImage): + fw.header.hash = fw.code_hash() elif isinstance(fw, firmware_headers.VendorFirmware): fw.firmware.header.hashes = fw.firmware.code_hashes() # else: do nothing, other kinds of images do not need rehashing @@ -100,8 +102,8 @@ def cli( """Manage firmware headers. This tool supports three types of files: raw vendor headers (TRZV), bootloader - images (TRZB), and firmware images which are prefixed with a vendor header - (TRZV+TRZF). + images (TRZB), firmware images which are prefixed with a vendor header + (TRZV+TRZF), and secmon images (TSEC). Run with no options on a file to dump information about that file. diff --git a/python/src/trezorlib/firmware/__init__.py b/python/src/trezorlib/firmware/__init__.py index 1c36ba9acc..d3d49d9238 100644 --- a/python/src/trezorlib/firmware/__init__.py +++ b/python/src/trezorlib/firmware/__init__.py @@ -33,6 +33,7 @@ if True: from .consts import * # noqa: F401, F403 from .core import * # noqa: F401, F403 from .legacy import * # noqa: F401, F403 + from .secmon import * # noqa: F401, F403 from .util import ( # noqa: F401 FirmwareIntegrityError, InvalidSignatureError, diff --git a/python/src/trezorlib/firmware/secmon.py b/python/src/trezorlib/firmware/secmon.py new file mode 100644 index 0000000000..7db05227a0 --- /dev/null +++ b/python/src/trezorlib/firmware/secmon.py @@ -0,0 +1,116 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2022 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +from __future__ import annotations + +from copy import copy + +import construct as c +from construct_classes import Struct, subcon + +from ..tools import EnumAdapter, TupleAdapter +from . import util +from .models import Model + +__all__ = [ + "SecmonHeader", + "SecmonImage", +] + + +class SecmonHeader(Struct): + header_len: int + code_length: int + version: tuple[int, int, int, int] + hw_model: Model | bytes + hw_revision: int + hash: bytes + + sigmask: int + signature: bytes + + # fmt: off + SUBCON = c.Struct( + "_start_offset" / c.Tell, + "magic" / c.Const(b"TSEC"), + "header_len" / c.Int32ul, + "code_length" / c.Int32ul, + "version" / TupleAdapter(c.Int8ul, c.Int8ul, c.Int8ul, c.Int8ul), + "hw_model" / EnumAdapter(c.Bytes(4), Model), + "hw_revision" / c.Int8ul, + "_reserved" / c.Padding(3), + "hash" / c.Bytes(32), + + "_reserved" / c.Padding(391), + "sigmask" / c.Byte, + "signature" / c.Bytes(64), + + "_end_offset" / c.Tell, + + "_rebuild_header_len" / c.Pointer( + c.this._start_offset + 4, + c.Rebuild(c.Int32ul, c.this._end_offset - c.this._start_offset) + ), + ) + # fmt: on + + +class SecmonImage(Struct): + """Raw secmon image. + + Consists of secmon header and code block. + """ + + header: SecmonHeader = subcon(SecmonHeader) + _header_end: int + _code_offset: int + code: bytes + + SUBCON = c.Struct( + "header" / SecmonHeader.SUBCON, + "code" / c.Bytes(c.this.header.code_length), + c.Terminated, + ) + + def get_hash_params(self) -> util.FirmwareHashParameters: + return Model.from_hw_model(self.header.hw_model).hash_params() + + def code_hash(self) -> bytes: + """Calculate hash of `code`.""" + + hash_params = self.get_hash_params() + + hash = hash_params.hash_function(self.code).digest() + + return hash + + def validate_code_hash(self) -> None: + if self.code_hash() != self.header.hash: + raise util.FirmwareIntegrityError("Invalid firmware data.") + + def digest(self) -> bytes: + hash_params = self.get_hash_params() + + header = copy(self.header) + header.hash = self.code_hash() + header.signature = b"\x00" * 64 + header.sigmask = 0 + return hash_params.hash_function(header.build()).digest() + + def model(self) -> Model | None: + if isinstance(self.header.hw_model, Model): + return self.header.hw_model + return None