You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/src/apps/management/apply_settings.py

318 lines
10 KiB

from typing import TYPE_CHECKING
from trezor import utils
from trezor.enums import ButtonRequestType
from trezor.ui.layouts import confirm_action, confirm_homescreen, confirm_single
from trezor.wire import DataError
import trezorui2
if TYPE_CHECKING:
from trezor.messages import ApplySettings, Success
from trezor.wire import Context, GenericContext
from trezor.enums import SafetyCheckLevel
BRT_PROTECT_CALL = ButtonRequestType.ProtectCall # CACHE
if utils.MODEL in ("1", "R"):
def _validate_homescreen_model_specific(homescreen: bytes) -> None:
from trezor.ui import WIDTH, HEIGHT
try:
w, h, is_grayscale = trezorui2.toif_info(homescreen)
except ValueError:
raise DataError("Invalid homescreen")
if w != WIDTH or h != HEIGHT:
raise DataError(f"Homescreen must be {WIDTH}x{HEIGHT} pixel large")
if not is_grayscale:
raise DataError("Homescreen must be grayscale")
else:
def _validate_homescreen_model_specific(homescreen: bytes) -> None:
from trezor.ui import WIDTH, HEIGHT
try:
w, h, mcu_height = trezorui2.jpeg_info(homescreen)
except ValueError:
raise DataError("Invalid homescreen")
if w != WIDTH or h != HEIGHT:
raise DataError(f"Homescreen must be {WIDTH}x{HEIGHT} pixel large")
if mcu_height > 16:
raise DataError("Unsupported jpeg type")
try:
trezorui2.jpeg_test(homescreen)
except ValueError:
raise DataError("Invalid homescreen")
def _validate_homescreen(homescreen: bytes) -> None:
import storage.device as storage_device
if homescreen == b"":
return
if len(homescreen) > storage_device.HOMESCREEN_MAXSIZE:
raise DataError(
f"Homescreen is too large, maximum size is {storage_device.HOMESCREEN_MAXSIZE} bytes"
)
_validate_homescreen_model_specific(homescreen)
async def apply_settings(ctx: Context, msg: ApplySettings) -> Success:
import storage.device as storage_device
from apps.common import safety_checks
from trezor.messages import Success
from trezor.wire import ProcessError, NotInitialized
from apps.base import reload_settings_from_storage
if not storage_device.is_initialized():
raise NotInitialized("Device is not initialized")
homescreen = msg.homescreen # local_cache_attribute
label = msg.label # local_cache_attribute
auto_lock_delay_ms = msg.auto_lock_delay_ms # local_cache_attribute
use_passphrase = msg.use_passphrase # local_cache_attribute
passphrase_always_on_device = (
msg.passphrase_always_on_device
) # local_cache_attribute
display_rotation = msg.display_rotation # local_cache_attribute
msg_safety_checks = msg.safety_checks # local_cache_attribute
experimental_features = msg.experimental_features # local_cache_attribute
hide_passphrase_from_host = msg.hide_passphrase_from_host # local_cache_attribute
if (
homescreen is None
and label is None
and use_passphrase is None
and passphrase_always_on_device is None
and display_rotation is None
and auto_lock_delay_ms is None
and msg_safety_checks is None
and experimental_features is None
and hide_passphrase_from_host is None
):
raise ProcessError("No setting provided")
if homescreen is not None:
_validate_homescreen(homescreen)
await _require_confirm_change_homescreen(ctx, homescreen)
try:
storage_device.set_homescreen(homescreen)
except ValueError:
raise DataError("Invalid homescreen")
if label is not None:
if len(label) > storage_device.LABEL_MAXLENGTH:
raise DataError("Label too long")
await _require_confirm_change_label(ctx, label)
storage_device.set_label(label)
if use_passphrase is not None:
await _require_confirm_change_passphrase(ctx, use_passphrase)
storage_device.set_passphrase_enabled(use_passphrase)
if passphrase_always_on_device is not None:
if not storage_device.is_passphrase_enabled():
raise DataError("Passphrase is not enabled")
await _require_confirm_change_passphrase_source(
ctx, passphrase_always_on_device
)
storage_device.set_passphrase_always_on_device(passphrase_always_on_device)
if auto_lock_delay_ms is not None:
if auto_lock_delay_ms < storage_device.AUTOLOCK_DELAY_MINIMUM:
raise ProcessError("Auto-lock delay too short")
if auto_lock_delay_ms > storage_device.AUTOLOCK_DELAY_MAXIMUM:
raise ProcessError("Auto-lock delay too long")
await _require_confirm_change_autolock_delay(ctx, auto_lock_delay_ms)
storage_device.set_autolock_delay_ms(auto_lock_delay_ms)
if msg_safety_checks is not None:
await _require_confirm_safety_checks(ctx, msg_safety_checks)
safety_checks.apply_setting(msg_safety_checks)
if display_rotation is not None:
await _require_confirm_change_display_rotation(ctx, display_rotation)
storage_device.set_rotation(display_rotation)
if experimental_features is not None:
await _require_confirm_experimental_features(ctx, experimental_features)
storage_device.set_experimental_features(experimental_features)
if hide_passphrase_from_host is not None:
if safety_checks.is_strict():
raise ProcessError("Safety checks are strict")
await _require_confirm_hide_passphrase_from_host(ctx, hide_passphrase_from_host)
storage_device.set_hide_passphrase_from_host(hide_passphrase_from_host)
reload_settings_from_storage()
return Success(message="Settings applied")
async def _require_confirm_change_homescreen(
ctx: GenericContext, homescreen: bytes
) -> None:
if homescreen == b"":
await confirm_action(
ctx,
"set_homescreen",
"Set homescreen",
description="Do you really want to set default homescreen image?",
br_code=BRT_PROTECT_CALL,
)
else:
await confirm_homescreen(
ctx,
homescreen,
)
async def _require_confirm_change_label(ctx: GenericContext, label: str) -> None:
await confirm_single(
ctx,
"set_label",
"Device name",
description="Change device name to {}?",
description_param=label,
verb="Change",
)
async def _require_confirm_change_passphrase(ctx: GenericContext, use: bool) -> None:
on_or_off = "on" if use else "off"
description = f"Turn {on_or_off} passphrase protection?"
verb = f"Turn {on_or_off}"
await confirm_action(
ctx,
"set_passphrase",
"Passphrase settings",
description=description,
verb=verb,
br_code=BRT_PROTECT_CALL,
)
async def _require_confirm_change_passphrase_source(
ctx: GenericContext, passphrase_always_on_device: bool
) -> None:
description = (
"Do you really want to enter passphrase always on the device?"
if passphrase_always_on_device
else "Do you want to revoke the passphrase on device setting?"
)
await confirm_action(
ctx,
"set_passphrase_source",
"Passphrase source",
description=description,
br_code=BRT_PROTECT_CALL,
)
async def _require_confirm_change_display_rotation(
ctx: GenericContext, rotation: int
) -> None:
if rotation == 0:
label = "north"
elif rotation == 90:
label = "east"
elif rotation == 180:
label = "south"
elif rotation == 270:
label = "west"
else:
raise DataError("Unsupported display rotation")
await confirm_action(
ctx,
"set_rotation",
"Change rotation",
description="Do you want to change device rotation to {}?",
description_param=label,
br_code=BRT_PROTECT_CALL,
)
async def _require_confirm_change_autolock_delay(
ctx: GenericContext, delay_ms: int
) -> None:
from trezor.strings import format_duration_ms
await confirm_action(
ctx,
"set_autolock_delay",
"Auto-lock delay",
description="Do you really want to auto-lock your device after {}?",
description_param=format_duration_ms(delay_ms),
br_code=BRT_PROTECT_CALL,
)
async def _require_confirm_safety_checks(
ctx: GenericContext, level: SafetyCheckLevel
) -> None:
from trezor.enums import SafetyCheckLevel
if level == SafetyCheckLevel.Strict:
await confirm_action(
ctx,
"set_safety_checks",
"Safety checks",
description="Do you really want to enforce strict safety checks (recommended)?",
br_code=BRT_PROTECT_CALL,
)
elif level in (SafetyCheckLevel.PromptAlways, SafetyCheckLevel.PromptTemporarily):
# Reusing most stuff for both levels
template = (
"Trezor will{}allow you to approve some actions which might be unsafe."
)
description = template.format(
" temporarily " if level == SafetyCheckLevel.PromptTemporarily else " "
)
await confirm_action(
ctx,
"set_safety_checks",
"Safety override",
"Are you sure?",
description,
hold=True,
verb="Hold to confirm",
reverse=True,
br_code=BRT_PROTECT_CALL,
)
else:
raise ValueError # enum value out of range
async def _require_confirm_experimental_features(
ctx: GenericContext, enable: bool
) -> None:
if enable:
await confirm_action(
ctx,
"set_experimental_features",
"Experimental mode",
"Only for development and beta testing!",
"Enable experimental features?",
reverse=True,
br_code=BRT_PROTECT_CALL,
)
async def _require_confirm_hide_passphrase_from_host(
ctx: GenericContext, enable: bool
) -> None:
if enable:
await confirm_action(
ctx,
"set_hide_passphrase_from_host",
"Hide passphrase",
description="Hide passphrase coming from host?",
br_code=BRT_PROTECT_CALL,
)