feat(core): add iterative data confirmation flows
What changed, and why it matters
This commit adds new UI helper functions across four Trezor device themes to let users confirm long pieces of data (like transaction details or messages) in chunks, showing a prefix of bytes and asking whether to continue or confirm all at once. It is a user-interface feature, not a fix for a known security bug. There is no direct evidence in the commit or supplied references that it addresses a vulnerability.
No immediate security action required. Treat as a normal feature commit. If reviewing for security, verify that callers of `confirm_blob_prefix()` correctly handle the returned `None` vs integer values and that the slicing bounds do not leak unintended data across memoryviews.
Security signals we found
No security-relevant keywords in commit title or message
No changelog entry provided
No caller code or integration shown in diff
No references to CVEs, advisories, or security reports supplied
Code is purely UI/UX helper logic using existing trusted primitives
Evidence from the diff
The patch introduces confirm_blob_prefix() in four layout modules (bolt, caesar, delizia, eckhart). Each implementation slices a memoryview of data to a theme-specific prefix size, displays it as hex, and uses the existing should_show_more() flow to let the user page through the rest or confirm everything. The function returns the number of bytes confirmed or None to skip confirmation. No callers are shown in the diff, and no security-relevant description is provided.
Changed components
core/src/trezor/ui/layouts/bolt/__init__.pycore/src/trezor/ui/layouts/caesar/__init__.pycore/src/trezor/ui/layouts/delizia/__init__.pycore/src/trezor/ui/layouts/eckhart/__init__.pyInspect captured patch +128 / −0
diff --git a/core/src/trezor/ui/layouts/bolt/__init__.py b/core/src/trezor/ui/layouts/bolt/__init__.py
index c7cb9c8f..3c6ec91e 100644
--- a/core/src/trezor/ui/layouts/bolt/__init__.py
+++ b/core/src/trezor/ui/layouts/bolt/__init__.py
@@ -1,3 +1,4 @@
+from micropython import const
from typing import TYPE_CHECKING
import trezorui_api
@@ -715,6 +716,45 @@ async def _confirm_ask_pagination(
assert False
+_INFO_DATA_ROWS = const(3)
+_INFO_DATA_WIDTH_BYTES = const(9)
+
+
+async def confirm_blob_prefix(
+ title: str,
+ data: memoryview,
+ *,
+ total_len: int,
+ confirmed_len: int,
+ br_name: str,
+ br_code: ButtonRequestType = BR_CODE_OTHER,
+) -> int | None:
+ """
+ Returns the number of bytes confirmed, or `None` if confirmation should be skipped.
+ """
+ # 3 rows x 18 hex digits (the maximal width is 19 digits)
+ prefix = data[: _INFO_DATA_ROWS * _INFO_DATA_WIDTH_BYTES]
+ prefix_parts = (
+ prefix[i * _INFO_DATA_WIDTH_BYTES : (i + 1) * _INFO_DATA_WIDTH_BYTES]
+ for i in range(_INFO_DATA_ROWS)
+ )
+ confirmed_len += len(prefix)
+
+ button_text = TR.words__show_next if confirmed_len < total_len else ""
+
+ show_more = await should_show_more(
+ title=f"{title}:\n{confirmed_len} / {total_len} bytes",
+ items=[(utils.hexlify_if_bytes(part), True) for part in prefix_parts],
+ button_text=button_text, # will return True
+ confirm=TR.words__confirm_all, # will return False
+ br_name=br_name,
+ br_code=br_code,
+ )
+ if show_more:
+ return len(prefix)
+ return None
+
+
def confirm_blob(
br_name: str,
title: str,
diff --git a/core/src/trezor/ui/layouts/caesar/__init__.py b/core/src/trezor/ui/layouts/caesar/__init__.py
index 783b8d12..f635dec3 100644
--- a/core/src/trezor/ui/layouts/caesar/__init__.py
+++ b/core/src/trezor/ui/layouts/caesar/__init__.py
@@ -744,6 +744,36 @@ async def should_show_more(
raise ActionCancelled
+async def confirm_blob_prefix(
+ title: str,
+ data: memoryview,
+ *,
+ total_len: int,
+ confirmed_len: int,
+ br_name: str,
+ br_code: ButtonRequestType = BR_CODE_OTHER,
+) -> int | None:
+ """
+ Returns the number of bytes confirmed, or `None` if confirmation should be skipped.
+ """
+ prefix = data[: 4 * 9] # 4 rows x 18 hex digits
+ confirmed_len += len(prefix)
+
+ button_text = DOWN_ARROW if confirmed_len < total_len else ""
+
+ show_more = await should_show_more(
+ title=f"{title}: {confirmed_len} / {total_len} bytes",
+ para=[(utils.hexlify_if_bytes(prefix), True)],
+ button_text=button_text, # will return True
+ confirm=TR.words__confirm_all, # will return False
+ br_name=br_name,
+ br_code=br_code,
+ )
+ if show_more:
+ return len(prefix)
+ return None
+
+
def confirm_blob(
br_name: str,
title: str,
diff --git a/core/src/trezor/ui/layouts/delizia/__init__.py b/core/src/trezor/ui/layouts/delizia/__init__.py
index db7245ee..f548b788 100644
--- a/core/src/trezor/ui/layouts/delizia/__init__.py
+++ b/core/src/trezor/ui/layouts/delizia/__init__.py
@@ -712,6 +712,35 @@ async def should_show_more(
raise ActionCancelled
+async def confirm_blob_prefix(
+ title: str,
+ data: memoryview,
+ *,
+ total_len: int,
+ confirmed_len: int,
+ br_name: str,
+ br_code: ButtonRequestType = BR_CODE_OTHER,
+) -> int | None:
+ """
+ Returns the number of bytes confirmed, or `None` if confirmation should be skipped.
+ """
+ prefix = data[: 7 * 9] # 7 rows x 18 hex digits
+ confirmed_len += len(prefix)
+
+ show_more = not await should_show_more(
+ title=title,
+ subtitle=f"{confirmed_len} / {total_len} bytes",
+ para=[(utils.hexlify_if_bytes(prefix), True)],
+ confirm="", # will return False
+ button_text=TR.words__confirm_all, # will return True
+ br_name=br_name,
+ br_code=br_code,
+ )
+ if show_more:
+ return len(prefix)
+ return None
+
+
def confirm_blob(
br_name: str,
title: str,
diff --git a/core/src/trezor/ui/layouts/eckhart/__init__.py b/core/src/trezor/ui/layouts/eckhart/__init__.py
index 6f5d3cfb..a7eac0f1 100644
--- a/core/src/trezor/ui/layouts/eckhart/__init__.py
+++ b/core/src/trezor/ui/layouts/eckhart/__init__.py
@@ -725,6 +725,35 @@ async def should_show_more(
raise ActionCancelled
+async def confirm_blob_prefix(
+ title: str,
+ data: memoryview,
+ *,
+ total_len: int,
+ confirmed_len: int,
+ br_name: str,
+ br_code: ButtonRequestType = BR_CODE_OTHER,
+) -> int | None:
+ """
+ Returns the number of bytes confirmed, or `None` if confirmation should be skipped.
+ """
+ prefix = data[: 9 * 9] # 9 rows x 18 hex digits
+ confirmed_len += len(prefix)
+ verb = TR.words__show_next if confirmed_len < total_len else TR.buttons__continue
+
+ show_more = not await should_show_more(
+ title=f"{title}:\n{confirmed_len} / {total_len} bytes",
+ para=[(utils.hexlify_if_bytes(prefix), True)],
+ confirm=verb, # will return False
+ button_text=TR.words__confirm_all, # will return True
+ br_name=br_name,
+ br_code=br_code,
+ )
+ if show_more:
+ return len(prefix)
+ return None
+
+
def confirm_blob(
br_name: str,
title: str,
Why this scored 14/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.