feat(core): implement micropython app_loader/cache wrapper
What changed, and why it matters
This commit adds a new MicroPython module called `trezorapp` that lets Python code create, write, finalize, and run third-party application images inside the Trezor firmware. It is a feature implementation rather than a bug fix, and there is no direct evidence in the commit that it fixes a security vulnerability. However, because it exposes low-level app loading and task control to Python, any weaknesses in the underlying `app_cache`/`app_loader` C code or in how Python callers use this wrapper could become security-relevant. The commit itself does not describe security relevance, credit a researcher, or acknowledge a vulnerability report.
Treat this as a new privileged API surface. Audit the underlying `io/app_cache.h` and `io/app_loader.h` implementations for bounds checking, handle lifetime management, and race conditions. Review all Python callers of `trezorapp` for unsafe use of `write`, `finalize`, `spawn_task`, and `unload`. Ensure the emulator-only `load_file` path cannot be reached in production builds and that file path validation is robust in test builds. Consider adding unit/integration tests that exercise error paths and invalid offsets/sizes.
Security signals we found
New native MicroPython surface for app cache and task lifecycle management
Raw write primitive exposed to Python (`AppImage.write(offset, data)`) with offset and length passed to C
Finalize primitive can accept or discard a loaded image from Python
Task spawn/unload primitives exposed to Python
Emulator-only `load_file` helper loads arbitrary file paths into app cache
No input sanitization visible beyond app hash length check
No vendor security disclosure, CVE, or researcher attribution in commit
Evidence from the diff
The change introduces core/embed/upymod/modtrezorapp/ with C bindings for app_cache_create_image, app_cache_write_image, app_cache_finalize_image, app_cache_load_file (emulator only), app_task_spawn, app_task_is_running, and app_task_unload. It wires the module into the firmware and unix builds when app_loading is in FEATURES_WANTED, exposes USE_APP_LOADING via trezorutils, and conditionally imports trezorapp from trezor.__init__. The wrapper validates the app hash length but otherwise passes offsets, buffers, sizes, and boolean accept/reject decisions straight to the underlying C implementation. No changelog entry is provided and the commit message is purely feature-oriented.
Changed components
core/embed/upymod/modtrezorapp/modtrezorapp.ccore/embed/upymod/modtrezorapp/modtrezorapp-image.hcore/embed/upymod/modtrezorapp/modtrezorapp-task.hcore/embed/upymod/modtrezorio/modtrezorio.ccore/embed/upymod/modtrezorutils/modtrezorutils.ccore/src/trezor/__init__.pycore/src/trezor/utils.pycore/SConscript.firmwarecore/SConscript.unixcore/embed/projects/firmware/mpconfigport.hcore/embed/projects/unix/mpconfigport.hInspect captured patch +424 / −5
diff --git a/core/SConscript.firmware b/core/SConscript.firmware
index f8f0d6c1..386d7aa3 100644
--- a/core/SConscript.firmware
+++ b/core/SConscript.firmware
@@ -281,7 +281,11 @@ if not PRODUCTION:
SOURCE_MOD += ['embed/rtl/printf.c']
if "app_loading" in FEATURES_WANTED:
- SOURCE_MOD += ['embed/api/trezor_api_v1_impl.c']
+ SOURCE_MOD += [
+ 'embed/api/trezor_api_v1_impl.c',
+ 'embed/upymod/modtrezorapp/modtrezorapp.c',
+ ]
+
CPPDEFINES_MOD += [
'TRANSLATIONS',
diff --git a/core/SConscript.unix b/core/SConscript.unix
index 28964f1c..3141a47c 100644
--- a/core/SConscript.unix
+++ b/core/SConscript.unix
@@ -430,7 +430,10 @@ SOURCE_UNIX = [
]
if "app_loading" in FEATURES_WANTED:
- SOURCE_UNIX += ['embed/api/trezor_api_v1_impl.c']
+ SOURCE_UNIX += [
+ 'embed/api/trezor_api_v1_impl.c',
+ 'embed/upymod/modtrezorapp/modtrezorapp.c',
+ ]
TRANSLATION_DATA = [
diff --git a/core/embed/projects/firmware/mpconfigport.h b/core/embed/projects/firmware/mpconfigport.h
index 55463904..7d38157a 100644
--- a/core/embed/projects/firmware/mpconfigport.h
+++ b/core/embed/projects/firmware/mpconfigport.h
@@ -172,6 +172,7 @@
#define MICROPY_PY_TREZORPROTO (1)
#define MICROPY_PY_TREZORTRANSLATE (1)
#define MICROPY_PY_TREZORUI_API (1)
+#define MICROPY_PY_TREZORAPP (USE_APP_LOADING)
#define MP_STATE_PORT MP_STATE_VM
diff --git a/core/embed/projects/unix/mpconfigport.h b/core/embed/projects/unix/mpconfigport.h
index 0485f1b7..70a8beb0 100644
--- a/core/embed/projects/unix/mpconfigport.h
+++ b/core/embed/projects/unix/mpconfigport.h
@@ -221,6 +221,7 @@ extern const struct _mp_print_t mp_stderr_print;
#define MICROPY_PY_TREZORPROTO (1)
#define MICROPY_PY_TREZORTRANSLATE (1)
#define MICROPY_PY_TREZORUI_API (1)
+#define MICROPY_PY_TREZORAPP (USE_APP_LOADING)
#define MP_STATE_PORT MP_STATE_VM
diff --git a/core/embed/upymod/modtrezorapp/modtrezorapp-image.h b/core/embed/upymod/modtrezorapp/modtrezorapp-image.h
new file mode 100644
index 00000000..1bef6d19
--- /dev/null
+++ b/core/embed/upymod/modtrezorapp/modtrezorapp-image.h
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <trezor_rtl.h>
+
+#include <io/app_loader.h>
+
+/// package: trezorapp.__init__
+
+/// class AppImage:
+/// """
+/// Application image image.
+/// """
+typedef struct _mp_obj_AppImage_t {
+ mp_obj_base_t base;
+ app_cache_handle_t image;
+} mp_obj_AppImage_t;
+
+/// def write(self, offset: int, data: AnyBytes) -> None
+/// """
+/// Writes data to the application image at the specified offset.
+/// """
+STATIC mp_obj_t mod_trezorapp_AppImage_write(mp_obj_t self, mp_obj_t offset_obj,
+ mp_obj_t data_obj) {
+ mp_obj_AppImage_t *o = MP_OBJ_TO_PTR(self);
+ app_cache_handle_t image = o->image;
+
+ mp_buffer_info_t bufinfo = {0};
+ mp_get_buffer_raise(data_obj, &bufinfo, MP_BUFFER_READ);
+
+ uintptr_t offset = mp_obj_get_int(offset_obj);
+
+ if (!app_cache_write_image(image, offset, bufinfo.buf, bufinfo.len)) {
+ mp_raise_msg(&mp_type_RuntimeError,
+ MP_ERROR_TEXT("Failed to write to app image."));
+ }
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorapp_AppImage_write_obj,
+ mod_trezorapp_AppImage_write);
+
+/// def finalize(self, bool accept) -> None:
+/// """
+/// Finalizes loading of the application image. If `accept` is true,
+/// the image is marked as loaded and will be available for execution.
+/// If `accept` is false, the image is discarded.
+/// """
+STATIC mp_obj_t mod_trezorapp_AppImage_finalize(mp_obj_t self,
+ mp_obj_t accept_obj) {
+ mp_obj_AppImage_t *o = MP_OBJ_TO_PTR(self);
+
+ bool accept = mp_obj_is_true(accept_obj);
+
+ app_cache_finalize_image(o->image, accept);
+ o->image = APP_CACHE_INVALID_HANDLE;
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorapp_AppImage_finalize_obj,
+ mod_trezorapp_AppImage_finalize);
+
+STATIC const mp_rom_map_elem_t mod_trezorapp_AppImage_locals_dict_table[] = {
+ {MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mod_trezorapp_AppImage_write_obj)},
+ {MP_ROM_QSTR(MP_QSTR_finalize),
+ MP_ROM_PTR(&mod_trezorapp_AppImage_finalize_obj)},
+};
+STATIC MP_DEFINE_CONST_DICT(mod_trezorapp_AppImage_locals_dict,
+ mod_trezorapp_AppImage_locals_dict_table);
+
+STATIC const mp_obj_type_t mod_trezorapp_AppImage_type = {
+ {&mp_type_type},
+ .name = MP_QSTR_AppImage,
+ .locals_dict = (void *)&mod_trezorapp_AppImage_locals_dict,
+};
diff --git a/core/embed/upymod/modtrezorapp/modtrezorapp-task.h b/core/embed/upymod/modtrezorapp/modtrezorapp-task.h
new file mode 100644
index 00000000..ddfd4179
--- /dev/null
+++ b/core/embed/upymod/modtrezorapp/modtrezorapp-task.h
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <trezor_rtl.h>
+
+#include <io/app_loader.h>
+
+/// package: trezorapp.__init__
+
+/// class AppTask:
+/// """
+/// App task structure.
+/// """
+typedef struct _mp_obj_AppTask_t {
+ mp_obj_base_t base;
+ systask_id_t task_id;
+} mp_obj_AppTask_t;
+
+/// def id(self) -> int:
+/// """
+/// Returns the task id.
+/// """
+STATIC mp_obj_t mod_trezorapp_AppTask_id(mp_obj_t self) {
+ mp_obj_AppTask_t *o = MP_OBJ_TO_PTR(self);
+ return MP_OBJ_NEW_SMALL_INT(o->task_id);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorapp_AppTask_id_obj,
+ mod_trezorapp_AppTask_id);
+
+/// def is_running(self) -> bool:
+/// """
+/// Returns whether the application is still running.
+/// """
+STATIC mp_obj_t mod_trezorapp_AppTask_is_running(mp_obj_t self) {
+ mp_obj_AppTask_t *o = MP_OBJ_TO_PTR(self);
+
+ if (app_task_is_running(o->task_id)) {
+ return mp_const_true;
+ } else {
+ return mp_const_false;
+ }
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorapp_AppTask_is_running_obj,
+ mod_trezorapp_AppTask_is_running);
+
+/// def unload(self) -> None:
+/// """
+/// Unloads the application associated with this task.
+/// """
+STATIC mp_obj_t mod_trezorapp_AppTask_unload(mp_obj_t self) {
+ mp_obj_AppTask_t *o = MP_OBJ_TO_PTR(self);
+
+ app_task_unload(o->task_id);
+ o->task_id = 0;
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorapp_AppTask_unload_obj,
+ mod_trezorapp_AppTask_unload);
+
+STATIC const mp_rom_map_elem_t mod_trezorapp_AppTask_locals_dict_table[] = {
+ {MP_ROM_QSTR(MP_QSTR_id), MP_ROM_PTR(&mod_trezorapp_AppTask_id_obj)},
+ {MP_ROM_QSTR(MP_QSTR_is_running),
+ MP_ROM_PTR(&mod_trezorapp_AppTask_is_running_obj)},
+ {MP_ROM_QSTR(MP_QSTR_unload),
+ MP_ROM_PTR(&mod_trezorapp_AppTask_unload_obj)},
+};
+STATIC MP_DEFINE_CONST_DICT(mod_trezorapp_AppTask_locals_dict,
+ mod_trezorapp_AppTask_locals_dict_table);
+
+STATIC const mp_obj_type_t mod_trezorapp_AppTask_type = {
+ {&mp_type_type},
+ .name = MP_QSTR_AppTask,
+ .locals_dict = (void *)&mod_trezorapp_AppTask_locals_dict,
+};
diff --git a/core/embed/upymod/modtrezorapp/modtrezorapp.c b/core/embed/upymod/modtrezorapp/modtrezorapp.c
new file mode 100644
index 00000000..f82a7169
--- /dev/null
+++ b/core/embed/upymod/modtrezorapp/modtrezorapp.c
@@ -0,0 +1,150 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <trezor_rtl.h>
+
+#include <unistd.h>
+
+#include "py/mphal.h"
+#include "py/objstr.h"
+#include "py/runtime.h"
+
+#if MICROPY_PY_TREZORAPP
+
+#include <io/app_cache.h>
+#include <io/app_loader.h>
+
+#include "embed/upymod/trezorobj.h"
+
+#include "modtrezorapp-image.h"
+#include "modtrezorapp-task.h"
+
+/// package: trezorapp.__init__
+
+/// def spawn_task(app_hash: bytes) -> AppTask:
+/// """
+/// Spawns an application task from the app cache.
+/// """
+STATIC mp_obj_t mod_trezorapp_spawn_task(mp_obj_t app_hash_obj) {
+ mp_buffer_info_t hash = {0};
+ mp_get_buffer_raise(app_hash_obj, &hash, MP_BUFFER_READ);
+
+ if (hash.len != sizeof(app_hash_t)) {
+ mp_raise_ValueError(MP_ERROR_TEXT("Invalid app hash size"));
+ }
+
+ const app_hash_t *hash_ptr = (const app_hash_t *)hash.buf;
+
+ systask_id_t task_id;
+ if (!app_task_spawn(hash_ptr, &task_id)) {
+ mp_raise_msg(&mp_type_RuntimeError,
+ MP_ERROR_TEXT("Failed to spawn app from app cache"));
+ }
+
+ mp_obj_AppTask_t *o =
+ mp_obj_malloc(mp_obj_AppTask_t, &mod_trezorapp_AppTask_type);
+ o->task_id = task_id;
+ return MP_OBJ_FROM_PTR(o);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorapp_spawn_task_obj,
+ mod_trezorapp_spawn_task);
+
+/// def create_image(app_hash: bytes, size: int) -> AppImage:
+/// """
+/// Creates a new application image in the app cache.
+/// """
+STATIC mp_obj_t mod_trezorapp_create_image(mp_obj_t app_hash_obj,
+ mp_obj_t size_obj) {
+ mp_buffer_info_t hash = {0};
+ mp_get_buffer_raise(app_hash_obj, &hash, MP_BUFFER_READ);
+
+ if (hash.len != sizeof(app_hash_t)) {
+ mp_raise_ValueError(MP_ERROR_TEXT("Invalid app hash size"));
+ }
+
+ const app_hash_t *hash_ptr = (const app_hash_t *)hash.buf;
+
+ size_t size = mp_obj_get_int(size_obj);
+
+ app_cache_handle_t image = app_cache_create_image(hash_ptr, size);
+
+ if (image == APP_CACHE_INVALID_HANDLE) {
+ mp_raise_msg(&mp_type_RuntimeError,
+ MP_ERROR_TEXT("Failed to create app image in app cache"));
+ }
+ mp_obj_AppImage_t *o =
+ mp_obj_malloc(mp_obj_AppImage_t, &mod_trezorapp_AppImage_type);
+ o->image = image;
+ return MP_OBJ_FROM_PTR(o);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorapp_create_image_obj,
+ mod_trezorapp_create_image);
+
+#ifdef TREZOR_EMULATOR
+/// def load_file(app_hash: bytes, filename: Str) -> None:
+/// """
+/// Loads an application image from a file into the app cache.
+/// """
+STATIC mp_obj_t mod_trezorapp_load_file(mp_obj_t app_hash_obj,
+ mp_obj_t filename_obj) {
+ mp_buffer_info_t hash = {0};
+ mp_get_buffer_raise(app_hash_obj, &hash, MP_BUFFER_READ);
+
+ if (hash.len != sizeof(app_hash_t)) {
+ mp_raise_ValueError(MP_ERROR_TEXT("Invalid app hash size"));
+ }
+
+ const app_hash_t *hash_ptr = (const app_hash_t *)hash.buf;
+
+ const char *filename = mp_obj_str_get_str(filename_obj);
+
+ if (!app_cache_load_file(hash_ptr, filename)) {
+ mp_raise_msg(&mp_type_RuntimeError,
+ MP_ERROR_TEXT("Failed to load app image from file"));
+ }
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorapp_load_file_obj,
+ mod_trezorapp_load_file);
+#endif // TREZOR_EMULATOR
+
+STATIC const mp_rom_map_elem_t mp_module_trezorapp_globals_table[] = {
+ {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorapp)},
+
+ {MP_ROM_QSTR(MP_QSTR_spawn_task),
+ MP_ROM_PTR(&mod_trezorapp_spawn_task_obj)},
+ {MP_ROM_QSTR(MP_QSTR_create_image),
+ MP_ROM_PTR(&mod_trezorapp_create_image_obj)},
+#ifdef TREZOR_EMULATOR
+ {MP_ROM_QSTR(MP_QSTR_load_file), MP_ROM_PTR(&mod_trezorapp_load_file_obj)},
+#endif
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_trezorapp_globals,
+ mp_module_trezorapp_globals_table);
+
+const mp_obj_module_t mp_module_trezorapp = {
+ .base = {&mp_type_module},
+ .globals = (mp_obj_dict_t *)&mp_module_trezorapp_globals,
+};
+
+MP_REGISTER_MODULE(MP_QSTR_trezorapp, mp_module_trezorapp);
+
+#endif // MICROPY_PY_TREZORAPP
diff --git a/core/embed/upymod/modtrezorio/modtrezorio.c b/core/embed/upymod/modtrezorio/modtrezorio.c
index 3260c2cf..1bea4688 100644
--- a/core/embed/upymod/modtrezorio/modtrezorio.c
+++ b/core/embed/upymod/modtrezorio/modtrezorio.c
@@ -70,7 +70,7 @@ uint32_t last_touch_sample_time = 0;
#include "modtrezorio-poll.h"
/// package: trezorio.__init__
-/// from . import fatfs, haptic, sdcard, ble, pm, rgb_led, ipc
+/// from . import fatfs, haptic, sdcard, ble, pm, rgb_led, ipc, app_cache
/// POLL_READ: int # wait until interface is readable and return read data
/// POLL_WRITE: int # wait until interface is writable
@@ -140,7 +140,6 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR_IPC2_EVENT), MP_ROM_INT(SYSHANDLE_IPC2)},
{MP_ROM_QSTR(MP_QSTR_ipc_send), MP_ROM_PTR(&mod_trezorio_ipc_send_obj)},
#endif
-
{MP_ROM_QSTR(MP_QSTR_USB), MP_ROM_PTR(&mod_trezorio_USB_type)},
{MP_ROM_QSTR(MP_QSTR_USBIF), MP_ROM_PTR(&mod_trezorio_USBIF_type)},
{MP_ROM_QSTR(MP_QSTR_USBIF_WIRE), MP_ROM_INT(SYSHANDLE_USB_WIRE)},
diff --git a/core/embed/upymod/modtrezorutils/modtrezorutils.c b/core/embed/upymod/modtrezorutils/modtrezorutils.c
index 5f64a67c..44ac582b 100644
--- a/core/embed/upymod/modtrezorutils/modtrezorutils.c
+++ b/core/embed/upymod/modtrezorutils/modtrezorutils.c
@@ -761,6 +761,8 @@ STATIC const mp_obj_tuple_t mod_trezorutils_version_obj = {
/// """Whether the hardware has a nRF chip."""
/// USE_DBG_CONSOLE: bool
/// """Whether a debug console is enabled."""
+/// USE_APP_LOADING: bool
+/// """Whether the firmware supports loading 3rd-party applications."""
/// MODEL: str
/// """Model name."""
/// MODEL_FULL_NAME: str
@@ -952,6 +954,11 @@ STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR_USE_DBG_CONSOLE), mp_const_true},
#else
{MP_ROM_QSTR(MP_QSTR_USE_DBG_CONSOLE), mp_const_false},
+#endif
+#ifdef USE_APP_LOADING
+ {MP_ROM_QSTR(MP_QSTR_USE_APP_LOADING), mp_const_true},
+#else
+ {MP_ROM_QSTR(MP_QSTR_USE_APP_LOADING), mp_const_false},
#endif
{MP_ROM_QSTR(MP_QSTR_MODEL), MP_ROM_PTR(&mod_trezorutils_model_name_obj)},
{MP_ROM_QSTR(MP_QSTR_MODEL_FULL_NAME),
diff --git a/core/mocks/generated/trezorapp/__init__.pyi b/core/mocks/generated/trezorapp/__init__.pyi
new file mode 100644
index 00000000..99328980
--- /dev/null
+++ b/core/mocks/generated/trezorapp/__init__.pyi
@@ -0,0 +1,64 @@
+from typing import *
+from buffer_types import *
+
+
+# upymod/modtrezorapp/modtrezorapp-image.h
+class AppImage:
+ """
+ Application image image.
+ """
+
+ def write(self, offset: int, data: AnyBytes) -> None
+ """
+ Writes data to the application image at the specified offset.
+ """
+
+ def finalize(self, bool accept) -> None:
+ """
+ Finalizes loading of the application image. If `accept` is true,
+ the image is marked as loaded and will be available for execution.
+ If `accept` is false, the image is discarded.
+ """
+
+
+# upymod/modtrezorapp/modtrezorapp-task.h
+class AppTask:
+ """
+ App task structure.
+ """
+
+ def id(self) -> int:
+ """
+ Returns the task id.
+ """
+
+ def is_running(self) -> bool:
+ """
+ Returns whether the application is still running.
+ """
+
+ def unload(self) -> None:
+ """
+ Unloads the application associated with this task.
+ """
+
+
+# upymod/modtrezorapp/modtrezorapp.c
+def spawn_task(app_hash: bytes) -> AppTask:
+ """
+ Spawns an application task from the app cache.
+ """
+
+
+# upymod/modtrezorapp/modtrezorapp.c
+def create_image(app_hash: bytes, size: int) -> AppImage:
+ """
+ Creates a new application image in the app cache.
+ """
+
+
+# upymod/modtrezorapp/modtrezorapp.c
+def load_file(app_hash: bytes, filename: Str) -> None:
+ """
+ Loads an application image from a file into the app cache.
+ """
diff --git a/core/mocks/generated/trezorio/__init__.pyi b/core/mocks/generated/trezorio/__init__.pyi
index 1e87541e..e09e8b4d 100644
--- a/core/mocks/generated/trezorio/__init__.pyi
+++ b/core/mocks/generated/trezorio/__init__.pyi
@@ -113,7 +113,7 @@ class USB:
"""
Cleans up the USB stack.
"""
-from . import fatfs, haptic, sdcard, ble, pm, rgb_led, ipc
+from . import fatfs, haptic, sdcard, ble, pm, rgb_led, ipc, app_cache
POLL_READ: int # wait until interface is readable and return read data
POLL_WRITE: int # wait until interface is writable
diff --git a/core/mocks/generated/trezorutils.pyi b/core/mocks/generated/trezorutils.pyi
index 62bab0e8..d59a0c5f 100644
--- a/core/mocks/generated/trezorutils.pyi
+++ b/core/mocks/generated/trezorutils.pyi
@@ -266,6 +266,8 @@ USE_NRF: bool
"""Whether the hardware has a nRF chip."""
USE_DBG_CONSOLE: bool
"""Whether a debug console is enabled."""
+USE_APP_LOADING: bool
+"""Whether the firmware supports loading 3rd-party applications."""
MODEL: str
"""Model name."""
MODEL_FULL_NAME: str
diff --git a/core/src/trezor/__init__.py b/core/src/trezor/__init__.py
index 3fab1c84..70d6a6a9 100644
--- a/core/src/trezor/__init__.py
+++ b/core/src/trezor/__init__.py
@@ -4,3 +4,8 @@ import trezorio as io # noqa: F401
import trezortranslate as translations # noqa: F401
TR = translations.TR
+
+from . import utils
+
+if utils.USE_APP_LOADING:
+ import trezorapp as app # noqa: F401
diff --git a/core/src/trezor/utils.py b/core/src/trezor/utils.py
index 95cf0f44..062be0a3 100644
--- a/core/src/trezor/utils.py
+++ b/core/src/trezor/utils.py
@@ -21,6 +21,7 @@ from trezorutils import ( # noqa: F401
NOTIFY_WIPE,
SCM_REVISION,
UI_LAYOUT,
+ USE_APP_LOADING,
USE_BACKLIGHT,
USE_BLE,
USE_BUTTON,
Why this scored 32/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.