feat(core): passphrase reveal mode until touch end
What changed, and why it matters
This commit changes how Trezor devices reveal a passphrase on screen while typing. Previously, the revealed passphrase would hide again if the user's finger slid outside a specific extended area below the input box. Now, the passphrase stays visible until the user simply lifts their finger anywhere on the screen. This is a user-experience refinement, not a security fix or vulnerability.
No security action needed. Treat as a normal UX/product change during review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch removes the SHOWN_TOUCH_OUTSET extended touch area and the TouchMove handler that hid the passphrase when the finger left that area. It keeps the existing TouchStart (reveal) and TouchEnd (hide) behavior across three UI layouts (bolt, delizia, eckhart). The change simplifies input handling and makes reveal mode persist through finger movement until touch release.
Changed components
core/embed/rust/src/ui/layout_bolt/component/keyboard/passphrase.rscore/embed/rust/src/ui/layout_delizia/component/keyboard/passphrase.rscore/embed/rust/src/ui/layout_eckhart/firmware/keyboard/passphrase.rsInspect captured patch +5 / −47
diff --git a/core/embed/rust/src/ui/layout_bolt/component/keyboard/passphrase.rs b/core/embed/rust/src/ui/layout_bolt/component/keyboard/passphrase.rs
index 2763d7ea..b7128d48 100644
--- a/core/embed/rust/src/ui/layout_bolt/component/keyboard/passphrase.rs
+++ b/core/embed/rust/src/ui/layout_bolt/component/keyboard/passphrase.rs
@@ -407,7 +407,6 @@ impl Input {
const STYLE: TextStyle =
theme::label_keyboard().with_line_breaking(LineBreaking::BreakWordsNoHyphen);
const SHOWN_INSETS: Insets = Insets::new(8, 10, 8, 10);
- const SHOWN_TOUCH_OUTSET: Insets = Insets::bottom(80);
fn new(max_len: usize) -> Self {
Self {
@@ -538,11 +537,6 @@ impl Component for Input {
return None;
}
- let extended_shown_area = self
- .shown_area
- .outset(Self::SHOWN_TOUCH_OUTSET)
- .clamp(SCREEN);
-
match event {
// Reveal on touch start within the extended input area
Event::Touch(TouchEvent::TouchStart(pos)) if self.reveal_area.get().contains(pos) => {
@@ -553,19 +547,12 @@ impl Component for Input {
self.pad.clear();
ctx.request_paint();
}
+ // Hide on touch end anywhere on the screen
Event::Touch(TouchEvent::TouchEnd(_)) if self.display_style == DisplayStyle::Shown => {
self.display_style = DisplayStyle::Hidden;
self.pad.clear();
ctx.request_paint();
}
- Event::Touch(TouchEvent::TouchMove(pos))
- if !extended_shown_area.contains(pos)
- && self.display_style == DisplayStyle::Shown =>
- {
- self.display_style = DisplayStyle::Hidden;
- self.pad.clear();
- ctx.request_paint();
- }
// Timeout for showing the last char
Event::Timer(_) if self.last_char_timer.expire(event) => {
self.display_style = DisplayStyle::Hidden;
diff --git a/core/embed/rust/src/ui/layout_delizia/component/keyboard/passphrase.rs b/core/embed/rust/src/ui/layout_delizia/component/keyboard/passphrase.rs
index 59610ef8..76b3dbcf 100644
--- a/core/embed/rust/src/ui/layout_delizia/component/keyboard/passphrase.rs
+++ b/core/embed/rust/src/ui/layout_delizia/component/keyboard/passphrase.rs
@@ -477,7 +477,6 @@ impl Input {
const STYLE: TextStyle =
theme::label_keyboard().with_line_breaking(LineBreaking::BreakWordsNoHyphen);
const SHOWN_INSETS: Insets = Insets::new(8, 10, 8, 10);
- const SHOWN_TOUCH_OUTSET: Insets = Insets::bottom(80);
fn new(max_len: usize) -> Self {
Self {
@@ -626,12 +625,8 @@ impl Component for Input {
return None;
}
- let extended_shown_area = self
- .shown_area
- .outset(Self::SHOWN_TOUCH_OUTSET)
- .clamp(SCREEN);
-
match event {
+ // Reveal on touch start within the input area
Event::Touch(TouchEvent::TouchStart(pos)) if self.area.contains(pos) => {
self.multi_tap.clear_pending_state(ctx);
self.last_char_timer.stop();
@@ -640,19 +635,12 @@ impl Component for Input {
self.pad.clear();
ctx.request_paint();
}
+ // Hide on touch end anywhere on the screen
Event::Touch(TouchEvent::TouchEnd(_)) if self.display_style == DisplayStyle::Shown => {
self.display_style = DisplayStyle::Hidden;
self.pad.clear();
ctx.request_paint();
}
- Event::Touch(TouchEvent::TouchMove(pos))
- if !extended_shown_area.contains(pos)
- && self.display_style == DisplayStyle::Shown =>
- {
- self.display_style = DisplayStyle::Hidden;
- self.pad.clear();
- ctx.request_paint();
- }
// Timeout for showing the last char
Event::Timer(_) if self.last_char_timer.expire(event) => {
self.display_style = DisplayStyle::Hidden;
diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/keyboard/passphrase.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/keyboard/passphrase.rs
index 7a941305..d61001a9 100644
--- a/core/embed/rust/src/ui/layout_eckhart/firmware/keyboard/passphrase.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/firmware/keyboard/passphrase.rs
@@ -46,7 +46,6 @@ impl PassphraseInput {
const TWITCH: i16 = 4;
const STYLE: TextStyle =
theme::TEXT_REGULAR.with_line_breaking(LineBreaking::BreakWordsNoHyphen);
- const SHOWN_TOUCH_OUTSET: Insets = Insets::bottom(200);
const ICON: Icon = theme::ICON_DASH_VERTICAL;
const ICON_WIDTH: i16 = Self::ICON.toif.width();
const ICON_SPACE: i16 = 12;
@@ -298,13 +297,6 @@ impl Component for PassphraseInput {
return None;
}
- // Extend the passphrase area downward to allow touch input without the finger
- // covering the passphrase
- let extended_shown_area = self
- .shown_area
- .outset(Self::SHOWN_TOUCH_OUTSET)
- .clamp(SCREEN);
-
match event {
Event::Timer(_) if self.multi_tap.timeout_event(event) => {
self.multi_tap.clear_pending_state(ctx);
@@ -316,7 +308,7 @@ impl Component for PassphraseInput {
}
return None;
}
- // Return touch start if the touch is detected inside the touchable area
+ // Reveal on touch start within the input area
Event::Touch(TouchEvent::TouchStart(pos)) if self.area.contains(pos) => {
self.multi_tap.clear_pending_state(ctx);
// Stop the last char timer
@@ -326,21 +318,12 @@ impl Component for PassphraseInput {
self.update_shown_area();
return Some(StringInputMsg::UpdateKeypad);
}
- // Return touch end if the touch end is detected
+ // Hide on touch end anywhere on the screen
Event::Touch(TouchEvent::TouchEnd(_)) if self.display_style == DisplayStyle::Shown => {
self.multi_tap.clear_pending_state(ctx);
self.display_style = DisplayStyle::Hidden;
return Some(StringInputMsg::UpdateKeypad);
}
- // Return touch end if the touch moves out of the visible area
- Event::Touch(TouchEvent::TouchMove(pos))
- if !extended_shown_area.contains(pos)
- && self.display_style == DisplayStyle::Shown =>
- {
- self.multi_tap.clear_pending_state(ctx);
- self.display_style = DisplayStyle::Hidden;
- return Some(StringInputMsg::UpdateKeypad);
- }
// Timeout for showing the last char.
Event::Timer(_) if self.last_char_timer.expire(event) => {
self.display_style = DisplayStyle::Hidden;
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.