feat(core): Add USB communication wakeup source
What changed, and why it matters
This commit adds USB communication as a new way to wake a sleeping Trezor hardware wallet. Previously, the device could wake from button presses, Bluetooth, power events, NFC, or a real-time clock timer. Now, receiving data over the USB cable (WebUSB) also sets a wakeup flag. The change also restores the screen backlight to normal brightness when the device resumes from suspend. There is no indication in the commit that this fixes a security vulnerability; it appears to be a feature or usability improvement.
No immediate security action is required. Treat this as a normal feature commit. If reviewing for defense in depth, verify that waking from USB does not bypass the device's auto-lock or PIN-entry requirements, and that the backlight change does not leak sensitive information on screen before the user authenticates. These are policy questions outside the scope of the diff itself.
Security signals we found
New wakeup source added: USB WebUSB data reception
No authentication or authorization checks are added or removed
No buffer handling, parsing, or cryptographic code is modified
Backlight is restored on resume, which is a UI/UX change, not a security boundary
Commit title and message describe a feature, not a security fix
Evidence from the diff
The patch introduces WAKEUP_FLAG_USB in the suspend subsystem and exposes it to MicroPython. In usb_class_webusb.c, when a USB data-out callback completes (i.e., the host sends data to the device), wakeup_flags_set(WAKEUP_FLAG_USB) is called under USE_SUSPEND. The Python suspend handler adds WAKEUP_FLAG_USB to the set of handled wakeup sources. Additionally, lock_manager.py now calls backlight_fade(BacklightLevels.NORMAL) when resuming from suspend, likely so the screen is visible after a USB-initiated wake. The change is small, localized, and does not alter authentication, cryptography, or memory safety logic.
Changed components
core/embed/io/usb/stm32/usb_class_webusb.ccore/embed/sys/suspend/inc/sys/suspend.hcore/embed/upymod/modtrezorio/modtrezorio-pm.hcore/mocks/generated/trezorio/pm.pyicore/src/apps/common/lock_manager.pycore/src/trezor/power_management/suspend.pyInspect captured patch +16 / −2
diff --git a/core/embed/io/usb/stm32/usb_class_webusb.c b/core/embed/io/usb/stm32/usb_class_webusb.c
index f234842c0..e65694386 100644
--- a/core/embed/io/usb/stm32/usb_class_webusb.c
+++ b/core/embed/io/usb/stm32/usb_class_webusb.c
@@ -25,6 +25,10 @@
#include <sec/random_delays.h>
#include <sys/sysevent_source.h>
+#ifdef USE_SUSPEND
+#include <sys/suspend.h>
+#endif
+
#include "usb_internal.h"
#define USB_CLASS_WEBUSB 0xFF
@@ -249,6 +253,9 @@ static uint8_t usb_webusb_class_data_out(USBD_HandleTypeDef *dev,
// Save the report length to indicate we have read something, but don't
// schedule next reading until user reads this one
state->last_read_len = USBD_LL_GetRxDataSize(dev, ep_num);
+#ifdef USE_SUSPEND
+ wakeup_flags_set(WAKEUP_FLAG_USB);
+#endif
}
return USBD_OK;
diff --git a/core/embed/sys/suspend/inc/sys/suspend.h b/core/embed/sys/suspend/inc/sys/suspend.h
index a2cbf376e..6f345a4f5 100644
--- a/core/embed/sys/suspend/inc/sys/suspend.h
+++ b/core/embed/sys/suspend/inc/sys/suspend.h
@@ -26,9 +26,10 @@ typedef uint16_t wakeup_flags_t;
#define WAKEUP_FLAG_BUTTON (1 << 0) /** Button pressed */
#define WAKEUP_FLAG_POWER (1 << 1) /** Power up */
-#define WAKEUP_FLAG_BLE (1 << 2) /** Bluetooth connection event */
+#define WAKEUP_FLAG_BLE (1 << 2) /** Bluetooth communication */
#define WAKEUP_FLAG_NFC (1 << 3) /** NFC event */
#define WAKEUP_FLAG_RTC (1 << 4) /** RTC wake-up timer */
+#define WAKEUP_FLAG_USB (1 << 5) /** USB WIRE communication */
/**
* @brief Puts device into suspend mode (actually STOP2 mode on STM32U5)
diff --git a/core/embed/upymod/modtrezorio/modtrezorio-pm.h b/core/embed/upymod/modtrezorio/modtrezorio-pm.h
index c2ac7a8e6..36466a573 100644
--- a/core/embed/upymod/modtrezorio/modtrezorio-pm.h
+++ b/core/embed/upymod/modtrezorio/modtrezorio-pm.h
@@ -27,6 +27,7 @@
/// WAKEUP_FLAG_BLE: int
/// WAKEUP_FLAG_NFC: int
/// WAKEUP_FLAG_RTC: int
+/// WAKEUP_FLAG_USB: int
///
/// # Power manager event flags:
/// EVENT_POWER_STATUS_CHANGED: int
@@ -114,6 +115,7 @@ STATIC const mp_rom_map_elem_t mod_trezorio_pm_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR_WAKEUP_FLAG_BLE), MP_ROM_INT(WAKEUP_FLAG_BLE)},
{MP_ROM_QSTR(MP_QSTR_WAKEUP_FLAG_NFC), MP_ROM_INT(WAKEUP_FLAG_NFC)},
{MP_ROM_QSTR(MP_QSTR_WAKEUP_FLAG_RTC), MP_ROM_INT(WAKEUP_FLAG_RTC)},
+ {MP_ROM_QSTR(MP_QSTR_WAKEUP_FLAG_USB), MP_ROM_INT(WAKEUP_FLAG_USB)},
// Power manager event flags
{MP_ROM_QSTR(MP_QSTR_EVENT_POWER_STATUS_CHANGED), MP_ROM_INT(1 << 0)},
diff --git a/core/mocks/generated/trezorio/pm.pyi b/core/mocks/generated/trezorio/pm.pyi
index d41115f77..0916cfaec 100644
--- a/core/mocks/generated/trezorio/pm.pyi
+++ b/core/mocks/generated/trezorio/pm.pyi
@@ -6,6 +6,7 @@ WAKEUP_FLAG_POWER: int
WAKEUP_FLAG_BLE: int
WAKEUP_FLAG_NFC: int
WAKEUP_FLAG_RTC: int
+WAKEUP_FLAG_USB: int
# Power manager event flags:
EVENT_POWER_STATUS_CHANGED: int
diff --git a/core/src/apps/common/lock_manager.py b/core/src/apps/common/lock_manager.py
index ea06e27f5..66f01423e 100644
--- a/core/src/apps/common/lock_manager.py
+++ b/core/src/apps/common/lock_manager.py
@@ -43,7 +43,7 @@ else:
set_homescreen()
def notify_suspend() -> None:
- """Signal that the the device should be suspended in the next cycle.
+ """Signal that the device should be suspended in the next cycle.
Notifies an asynchronous task to perform the suspend in a separate thread.
"""
@@ -76,6 +76,7 @@ else:
_SHOULD_SUSPEND = False
set_homescreen()
+ backlight_fade(BacklightLevels.NORMAL)
async def _suspend_and_resume_task() -> None:
"""Task to suspend Trezor and handle wakeup.
diff --git a/core/src/trezor/power_management/suspend.py b/core/src/trezor/power_management/suspend.py
index 53d85d538..9ef269432 100644
--- a/core/src/trezor/power_management/suspend.py
+++ b/core/src/trezor/power_management/suspend.py
@@ -3,6 +3,7 @@ from trezor import io, log
_HANDLED_WAKEUP_FLAGS = (
io.pm.WAKEUP_FLAG_BUTTON,
io.pm.WAKEUP_FLAG_BLE,
+ io.pm.WAKEUP_FLAG_USB,
io.pm.WAKEUP_FLAG_POWER,
)
@@ -13,6 +14,7 @@ if __debug__:
io.pm.WAKEUP_FLAG_POWER: "POWER",
io.pm.WAKEUP_FLAG_NFC: "NFC",
io.pm.WAKEUP_FLAG_RTC: "RTC",
+ io.pm.WAKEUP_FLAG_USB: "USB",
}
Why this scored 18/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.