chore(core): move `paired_cache` to `trezor.wire.thp`
What changed, and why it matters
This commit is a routine code cleanup: it moves a small helper module that stores Bluetooth pairing information from one folder to another and adds extra debug logging. There is no change to how the device protects secrets, pairs with phones, or handles untrusted input. It is not a security fix.
No security action required; treat as normal refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change relocates paired_cache from apps.thp.paired_cache to trezor.wire.thp.paired_cache, updates import paths, and moves the _cache_host_info helper into the new module (renamed cache_host_info). It also adds __debug__-guarded log.debug calls for cache load/store/hit events and removes one redundant debug log in device_menu.py. The functional behavior—loading, filtering by bonded MAC addresses, trimming strings, and storing ThpPairedCache protobuf entries—remains identical.
Changed components
core/src/apps/homescreen/device_menu.pycore/src/apps/thp/pairing.pycore/src/trezor/wire/thp/paired_cache.pycore/tests/test_apps.thp.paired_cache.pycore/embed/upymod/qstrdefsport.hInspect captured patch +83 / −61
diff --git a/core/embed/upymod/qstrdefsport.h b/core/embed/upymod/qstrdefsport.h
index 69ec0ef73..073f72eee 100644
--- a/core/embed/upymod/qstrdefsport.h
+++ b/core/embed/upymod/qstrdefsport.h
@@ -407,7 +407,6 @@ Q(ThpPairingMethod)
Q(alternating_bit_protocol)
Q(apps.thp)
Q(apps.thp.credential_manager)
-Q(apps.thp.paired_cache)
Q(apps.thp.pairing)
Q(cache_thp)
Q(cache_thp_keys)
@@ -441,6 +440,7 @@ Q(trezor.wire.thp.cpace)
Q(trezor.wire.thp.crypto)
Q(trezor.wire.thp.interface_context)
Q(trezor.wire.thp.memory_manager)
+Q(trezor.wire.thp.paired_cache)
Q(trezor.wire.thp.pairing_context)
Q(trezor.wire.thp.received_message_handler)
Q(trezor.wire.thp.session_context)
diff --git a/core/src/apps/homescreen/device_menu.py b/core/src/apps/homescreen/device_menu.py
index d595f49a3..c64d582df 100644
--- a/core/src/apps/homescreen/device_menu.py
+++ b/core/src/apps/homescreen/device_menu.py
@@ -66,7 +66,8 @@ def get_auto_lock_delay() -> tuple[str, str] | None:
async def handle_device_menu() -> None:
assert utils.USE_THP and utils.USE_BLE
- from ..thp import paired_cache
+
+ from trezor.wire.thp import paired_cache
init_submenu_idx = None
@@ -94,8 +95,6 @@ async def handle_device_menu() -> None:
if __debug__:
log.debug(__name__, "connected: %s (%s)", connected_addr, connected_idx)
hostname_map = {e.mac_addr: e for e in paired_cache.load()}
- if __debug__:
- log.debug(__name__, "hostname_map: %s", hostname_map)
paired_devices = [_get_hostinfo(bond, hostname_map) for bond in bonds]
if utils.USE_NRF:
diff --git a/core/src/apps/thp/paired_cache.py b/core/src/apps/thp/paired_cache.py
deleted file mode 100644
index 26da46e58..000000000
--- a/core/src/apps/thp/paired_cache.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from micropython import const
-
-from trezor.messages import ThpPairedCache, ThpPairedCacheEntry
-
-_ENABLE_EXPERIMENTAL = const(False)
-
-
-def load() -> list[ThpPairedCacheEntry]:
- """Load THP paired entries from flash."""
- from storage.device import get_thp_paired_cache
- from trezor.protobuf import decode
-
- if (blob := get_thp_paired_cache()) is None:
- return [] # an empty cache
-
- cache = decode(blob, ThpPairedCache, _ENABLE_EXPERIMENTAL)
- return cache.entries
-
-
-def store(entries: list[ThpPairedCacheEntry], _bonds: set[bytes] | None = None) -> None:
- """Store THP paired entries to flash."""
- from storage.device import set_thp_paired_cache
- from trezor.protobuf import dump_message_buffer
-
- if _bonds is None:
- from trezorble import get_bonds
-
- _bonds = set(get_bonds())
-
- # Remove entries with unbonded MAC addresses
- entries = [e for e in entries if e.mac_addr in _bonds]
-
- cache = ThpPairedCache(entries=entries)
- set_thp_paired_cache(dump_message_buffer(cache))
diff --git a/core/src/apps/thp/pairing.py b/core/src/apps/thp/pairing.py
index e8cc9b46d..c6e82f88d 100644
--- a/core/src/apps/thp/pairing.py
+++ b/core/src/apps/thp/pairing.py
@@ -41,6 +41,7 @@ from trezor.wire.thp import (
get_enabled_pairing_methods,
ui,
)
+from trezor.wire.thp.paired_cache import cache_host_info
from trezor.wire.thp.pairing_context import PairingContext
from .credential_manager import is_credential_autoconnect, issue_credential
@@ -120,8 +121,7 @@ async def handle_pairing_request(
await ui.show_pairing_dialog(message.host_name, message.app_name)
ctx.host_name = message.host_name
ctx.app_name = message.app_name
- if peer_addr is not None:
- _cache_host_info(peer_addr, ctx.host_name, ctx.app_name)
+ cache_host_info(peer_addr, host_name=ctx.host_name, app_name=ctx.app_name)
await ctx.write(ThpPairingRequestApproved())
assert ThpSelectMethod.MESSAGE_WIRE_TYPE is not None
@@ -479,22 +479,3 @@ def _check_method_is_allowed(ctx: PairingContext, method: ThpPairingMethod) -> N
def _check_method_is_selected(ctx: PairingContext, method: ThpPairingMethod) -> None:
if method is not ctx.selected_method:
raise ThpError("Not selected pairing method")
-
-
-def _cache_host_info(mac_addr: bytes, host_name: str, app_name: str) -> None:
- from trezor.messages import ThpPairedCacheEntry
- from trezor.strings import trim_str
-
- from . import paired_cache
-
- entries = paired_cache.load()
- if any(mac_addr == e.mac_addr for e in entries):
- # skip writing to flash if this MAC address is already cached
- return
-
- host_name = trim_str(host_name, max_bytes=32)
- app_name = trim_str(app_name, max_bytes=32)
- entries.append(
- ThpPairedCacheEntry(mac_addr=mac_addr, host_name=host_name, app_name=app_name)
- )
- paired_cache.store(entries)
diff --git a/core/src/trezor/wire/thp/paired_cache.py b/core/src/trezor/wire/thp/paired_cache.py
new file mode 100644
index 000000000..af5b91df7
--- /dev/null
+++ b/core/src/trezor/wire/thp/paired_cache.py
@@ -0,0 +1,77 @@
+from micropython import const
+from typing import TYPE_CHECKING
+
+from trezor.messages import ThpPairedCache, ThpPairedCacheEntry
+
+if __debug__:
+ from trezor import log, utils
+
+if TYPE_CHECKING:
+ from buffer_types import AnyBytes
+
+_ENABLE_EXPERIMENTAL = const(False)
+
+
+def load() -> list[ThpPairedCacheEntry]:
+ """Load THP paired entries from flash."""
+ from storage.device import get_thp_paired_cache
+ from trezor.protobuf import decode
+
+ if (blob := get_thp_paired_cache()) is None:
+ return [] # an empty cache
+
+ cache = decode(blob, ThpPairedCache, _ENABLE_EXPERIMENTAL)
+ if __debug__:
+ log.debug(__name__, "loaded THP cache:\n%s", utils.dump_protobuf(cache))
+
+ return cache.entries
+
+
+def store(entries: list[ThpPairedCacheEntry], _bonds: set[bytes] | None = None) -> None:
+ """Store THP paired entries to flash."""
+ from storage.device import set_thp_paired_cache
+ from trezor.protobuf import dump_message_buffer
+
+ if _bonds is None:
+ from trezorble import get_bonds
+
+ _bonds = set(get_bonds())
+
+ # Remove entries with unbonded MAC addresses
+ entries = [e for e in entries if e.mac_addr in _bonds]
+
+ cache = ThpPairedCache(entries=entries)
+ if __debug__:
+ log.debug(__name__, "storing THP cache:\n%s", utils.dump_protobuf(cache))
+
+ set_thp_paired_cache(dump_message_buffer(cache))
+
+
+def cache_host_info(mac_addr: AnyBytes | None, host_name: str, app_name: str) -> None:
+ if mac_addr is None:
+ if __debug__:
+ log.debug(__name__, "no MAC address: host=%s app=%s", host_name, app_name)
+ return
+
+ from trezor.messages import ThpPairedCacheEntry
+ from trezor.strings import trim_str
+
+ entries = load()
+ for e in entries:
+ if mac_addr == e.mac_addr:
+ if __debug__:
+ log.debug(
+ __name__,
+ "found cached MAC %s:\n%s",
+ mac_addr,
+ utils.dump_protobuf(e),
+ )
+ # skip writing to flash if this MAC address is already cached
+ return
+
+ host_name = trim_str(host_name, max_bytes=32)
+ app_name = trim_str(app_name, max_bytes=32)
+ entries.append(
+ ThpPairedCacheEntry(mac_addr=mac_addr, host_name=host_name, app_name=app_name)
+ )
+ store(entries)
diff --git a/core/tests/test_apps.thp.paired_cache.py b/core/tests/test_apps.thp.paired_cache.py
index 5d4ec0f5f..67154ab5e 100644
--- a/core/tests/test_apps.thp.paired_cache.py
+++ b/core/tests/test_apps.thp.paired_cache.py
@@ -5,8 +5,7 @@ from trezor import config, utils
if utils.USE_THP:
from storage.device import get_thp_paired_cache
from trezor.messages import ThpPairedCacheEntry
-
- from apps.thp import paired_cache
+ from trezor.wire.thp import paired_cache
ALL_ENTRIES = [
ThpPairedCacheEntry(
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.