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

282 lines
9.4 KiB

from typing import TYPE_CHECKING
import storage.device as storage_device
import trezorui2
from trezor import TR
from trezor import utils
from trezor.enums import ButtonRequestType
from trezor.ui import display, style
from trezor.ui.layouts import confirm_action, request_number_slider
from trezor.wire import DataError
if TYPE_CHECKING:
from trezor.enums import SafetyCheckLevel
from trezor.messages import ApplySettings, Success
BRT_PROTECT_CALL = ButtonRequestType.ProtectCall # CACHE
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"
)
if not trezorui2.check_homescreen_format(homescreen):
raise DataError("Wrong homescreen format")
async def apply_settings(msg: ApplySettings) -> Success:
from trezor.messages import Success
from trezor.wire import NotInitialized, ProcessError
from apps.base import reload_settings_from_storage
from apps.common import safety_checks
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
brightness = msg.brightness
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
and (brightness is None or not utils.USE_BACKLIGHT)
):
raise ProcessError("No setting provided")
if homescreen is not None:
_validate_homescreen(homescreen)
await _require_confirm_change_homescreen(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(label)
storage_device.set_label(label)
if use_passphrase is not None:
await _require_confirm_change_passphrase(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(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(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(msg_safety_checks)
safety_checks.apply_setting(msg_safety_checks)
if display_rotation is not None:
await _require_confirm_change_display_rotation(display_rotation)
storage_device.set_rotation(display_rotation)
if experimental_features is not None:
await _require_confirm_experimental_features(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(hide_passphrase_from_host)
storage_device.set_hide_passphrase_from_host(hide_passphrase_from_host)
if brightness is not None and utils.USE_BACKLIGHT:
new_brightness = await _require_set_brightness()
storage_device.set_brightness(new_brightness)
reload_settings_from_storage()
return Success(message="Settings applied")
async def _require_confirm_change_homescreen(homescreen: bytes) -> None:
from trezor.ui.layouts import confirm_homescreen
await confirm_homescreen(homescreen)
async def _require_confirm_change_label(label: str) -> None:
from trezor.ui.layouts import confirm_single
await confirm_single(
"set_label",
TR.device_name__title,
description=TR.device_name__change_template,
description_param=label,
verb=TR.buttons__change,
)
async def _require_confirm_change_passphrase(use: bool) -> None:
description = TR.passphrase__turn_on if use else TR.passphrase__turn_off
verb = TR.buttons__turn_on if use else TR.buttons__turn_off
await confirm_action(
"set_passphrase",
TR.passphrase__title_settings,
description=description,
verb=verb,
br_code=BRT_PROTECT_CALL,
)
async def _require_confirm_change_passphrase_source(
passphrase_always_on_device: bool,
) -> None:
description = (
TR.passphrase__always_on_device
if passphrase_always_on_device
else TR.passphrase__revoke_on_device
)
await confirm_action(
"set_passphrase_source",
TR.passphrase__title_source,
description=description,
br_code=BRT_PROTECT_CALL,
)
async def _require_confirm_change_display_rotation(rotation: int) -> None:
if rotation == 0:
label = TR.rotation__north
elif rotation == 90:
label = TR.rotation__east
elif rotation == 180:
label = TR.rotation__south
elif rotation == 270:
label = TR.rotation__west
else:
raise DataError("Unsupported display rotation")
await confirm_action(
"set_rotation",
TR.rotation__title_change,
description=TR.rotation__change_template,
description_param=label,
br_code=BRT_PROTECT_CALL,
)
async def _require_confirm_change_autolock_delay(delay_ms: int) -> None:
from trezor.strings import format_duration_ms
unit_plurals = {
"millisecond": TR.plurals__lock_after_x_milliseconds,
"second": TR.plurals__lock_after_x_seconds,
"minute": TR.plurals__lock_after_x_minutes,
"hour": TR.plurals__lock_after_x_hours,
}
await confirm_action(
"set_autolock_delay",
TR.auto_lock__title,
description=TR.auto_lock__change_template,
description_param=format_duration_ms(delay_ms, unit_plurals),
br_code=BRT_PROTECT_CALL,
)
async def _require_confirm_safety_checks(level: SafetyCheckLevel) -> None:
from trezor.enums import SafetyCheckLevel
if level == SafetyCheckLevel.Strict:
await confirm_action(
"set_safety_checks",
TR.safety_checks__title,
description=TR.safety_checks__enforce_strict,
br_code=BRT_PROTECT_CALL,
)
elif level in (SafetyCheckLevel.PromptAlways, SafetyCheckLevel.PromptTemporarily):
description = (
TR.safety_checks__approve_unsafe_temporary
if level == SafetyCheckLevel.PromptTemporarily
else TR.safety_checks__approve_unsafe_always
)
await confirm_action(
"set_safety_checks",
TR.safety_checks__title_safety_override,
TR.words__are_you_sure,
description,
hold=True,
verb=TR.buttons__hold_to_confirm,
reverse=True,
br_code=BRT_PROTECT_CALL,
)
else:
raise ValueError # enum value out of range
async def _require_confirm_experimental_features(enable: bool) -> None:
if enable:
await confirm_action(
"set_experimental_features",
TR.experimental_mode__title,
TR.experimental_mode__only_for_dev,
TR.experimental_mode__enable,
reverse=True,
br_code=BRT_PROTECT_CALL,
)
async def _require_confirm_hide_passphrase_from_host(enable: bool) -> None:
if enable:
await confirm_action(
"set_hide_passphrase_from_host",
TR.passphrase__title_hide,
description=TR.passphrase__hide,
br_code=BRT_PROTECT_CALL,
)
if utils.USE_BACKLIGHT:
async def _require_set_brightness() -> int:
def callback(val: int) -> None:
display.backlight(val)
return await request_number_slider(
"Set brightness",
callback,
min_count=style.BACKLIGHT_MIN,
max_count=style.BACKLIGHT_MAX,
count=style.get_backlight_normal(),
br_name="set_brightness",
)