feat: hide change output on legacy transaction
What changed, and why it matters
This commit changes how the Keystone 3 hardware wallet displays Bitcoin transaction recipients. Specifically, it hides the 'change' label on outputs that return bitcoin to the user's own wallet when the transaction comes from a 'legacy' format. This is a user-interface change, not a fix for stealing funds. It may reduce user clarity during transaction review, but it does not by itself allow an attacker to move coins.
Treat as a low-severity UX change. Review whether hiding change labels on legacy transactions could confuse users or be abused by a malicious companion app to make a payment-to-attacker output look like change. Ensure the overall transaction-review screen still clearly distinguishes outgoing payments from internal change.
Security signals we found
UI label suppression for change outputs
Conditional based on QR/UR transaction type
No cryptographic or signing code modified
Evidence from the diff
In src/ui/gui_chain/gui_btc.c, CreateOverviewToView() now sets showChange = false when the UR type is a legacy transaction type (the exact macro CHECK_UR_TYPE is not defined in the diff). As a result, outputs where to->data[i].is_mine is true no longer render the white ‘CHANGE’ badge. The transaction logic and signing are unchanged; only the on-screen label is suppressed. The commit title says ‘hide change output on legacy transaction’.
Changed components
src/ui/gui_chain/gui_btc.cBitcoin transaction overview screenChange-output UI badge renderingInspect captured patch +12 / −1
diff --git a/src/ui/gui_chain/gui_btc.c b/src/ui/gui_chain/gui_btc.c
index 4d64ef2..b8e2a1e 100644
--- a/src/ui/gui_chain/gui_btc.c
+++ b/src/ui/gui_chain/gui_btc.c
@@ -995,6 +995,17 @@ static lv_obj_t *CreateOverviewFromView(lv_obj_t *parent, DisplayTxOverview *ove
static lv_obj_t *CreateOverviewToView(lv_obj_t *parent, DisplayTxOverview *overviewData, lv_obj_t *lastView)
{
+ bool showChange = true;
+#ifndef BTC_ONLY
+ enum QRCodeType urType = URTypeUnKnown;
+ if (g_isMulti) {
+ urType = g_urMultiResult->ur_type;
+ } else {
+ urType = g_urResult->ur_type;
+ }
+
+ showChange = !CHECK_UR_TYPE();
+#endif
lv_obj_t *toContainer = GuiCreateContainerWithParent(parent, 408, 62);
SetContainerDefaultStyle(toContainer);
lv_obj_align_to(toContainer, lastView, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 16);
@@ -1043,7 +1054,7 @@ static lv_obj_t *CreateOverviewToView(lv_obj_t *parent, DisplayTxOverview *overv
toContainerHeight += (addressLabelHeight);
lv_obj_set_height(toInnerContainer, addressLabelHeight);
- if(to->data[i].is_mine) {
+ if(to->data[i].is_mine && showChange) {
lv_obj_t *changeContainer = GuiCreateContainerWithParent(toInnerContainer, 87, 30);
lv_obj_set_style_radius(changeContainer, 16, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_color(changeContainer, WHITE_COLOR, LV_PART_MAIN | LV_STATE_DEFAULT);
Why this scored 19/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.