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

244 lines
8.1 KiB

from typing import TYPE_CHECKING
import storage.device
from trezor import ui, wire
from trezor.enums import ButtonRequestType, SafetyCheckLevel
from trezor.messages import Success
from trezor.strings import format_duration_ms
from trezor.ui.layouts import confirm_action
from apps.base import reload_settings_from_storage
from apps.common import safety_checks
if TYPE_CHECKING:
from trezor.messages import ApplySettings
def validate_homescreen(homescreen: bytes) -> None:
if homescreen == b"":
return
if len(homescreen) > storage.device.HOMESCREEN_MAXSIZE:
raise wire.DataError(
f"Homescreen is too large, maximum size is {storage.device.HOMESCREEN_MAXSIZE} bytes"
)
try:
w, h, grayscale = ui.display.toif_info(homescreen)
except ValueError:
raise wire.DataError("Invalid homescreen")
if w != 144 or h != 144:
raise wire.DataError("Homescreen must be 144x144 pixel large")
if grayscale:
raise wire.DataError("Homescreen must be full-color TOIF image")
async def apply_settings(ctx: wire.Context, msg: ApplySettings) -> Success:
if not storage.device.is_initialized():
raise wire.NotInitialized("Device is not initialized")
if (
msg.homescreen is None
and msg.label is None
and msg.use_passphrase is None
and msg.passphrase_always_on_device is None
and msg.display_rotation is None
and msg.auto_lock_delay_ms is None
and msg.safety_checks is None
and msg.experimental_features is None
):
raise wire.ProcessError("No setting provided")
if msg.homescreen is not None:
validate_homescreen(msg.homescreen)
await require_confirm_change_homescreen(ctx)
try:
storage.device.set_homescreen(msg.homescreen)
except ValueError:
raise wire.DataError("Invalid homescreen")
if msg.label is not None:
if len(msg.label) > storage.device.LABEL_MAXLENGTH:
raise wire.DataError("Label too long")
await require_confirm_change_label(ctx, msg.label)
storage.device.set_label(msg.label)
if msg.use_passphrase is not None:
await require_confirm_change_passphrase(ctx, msg.use_passphrase)
storage.device.set_passphrase_enabled(msg.use_passphrase)
if msg.passphrase_always_on_device is not None:
if not storage.device.is_passphrase_enabled():
raise wire.DataError("Passphrase is not enabled")
await require_confirm_change_passphrase_source(
ctx, msg.passphrase_always_on_device
)
storage.device.set_passphrase_always_on_device(msg.passphrase_always_on_device)
if msg.auto_lock_delay_ms is not None:
if msg.auto_lock_delay_ms < storage.device.AUTOLOCK_DELAY_MINIMUM:
raise wire.ProcessError("Auto-lock delay too short")
if msg.auto_lock_delay_ms > storage.device.AUTOLOCK_DELAY_MAXIMUM:
raise wire.ProcessError("Auto-lock delay too long")
await require_confirm_change_autolock_delay(ctx, msg.auto_lock_delay_ms)
storage.device.set_autolock_delay_ms(msg.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 msg.display_rotation is not None:
await require_confirm_change_display_rotation(ctx, msg.display_rotation)
storage.device.set_rotation(msg.display_rotation)
if msg.experimental_features is not None:
await require_confirm_experimental_features(ctx, msg.experimental_features)
storage.device.set_experimental_features(msg.experimental_features)
reload_settings_from_storage()
return Success(message="Settings applied")
async def require_confirm_change_homescreen(ctx: wire.GenericContext) -> None:
await confirm_action(
ctx,
"set_homescreen",
"Set homescreen",
description="Do you really want to change the homescreen image?",
br_code=ButtonRequestType.ProtectCall,
)
async def require_confirm_change_label(ctx: wire.GenericContext, label: str) -> None:
await confirm_action(
ctx,
"set_label",
"Change label",
description="Do you really want to change the label to {}?",
description_param=label,
br_code=ButtonRequestType.ProtectCall,
)
async def require_confirm_change_passphrase(
ctx: wire.GenericContext, use: bool
) -> None:
if use:
description = "Do you really want to enable passphrase encryption?"
else:
description = "Do you really want to disable passphrase encryption?"
await confirm_action(
ctx,
"set_passphrase",
"Enable passphrase" if use else "Disable passphrase",
description=description,
br_code=ButtonRequestType.ProtectCall,
)
async def require_confirm_change_passphrase_source(
ctx: wire.GenericContext, passphrase_always_on_device: bool
) -> None:
if passphrase_always_on_device:
description = "Do you really want to enter passphrase always on the device?"
else:
description = "Do you want to revoke the passphrase on device setting?"
await confirm_action(
ctx,
"set_passphrase_source",
"Passphrase source",
description=description,
br_code=ButtonRequestType.ProtectCall,
)
async def require_confirm_change_display_rotation(
ctx: wire.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 wire.DataError("Unsupported display rotation")
await confirm_action(
ctx,
"set_rotation",
"Change rotation",
description="Do you really want to change display rotation to {}?",
description_param=label,
br_code=ButtonRequestType.ProtectCall,
)
async def require_confirm_change_autolock_delay(
ctx: wire.GenericContext, delay_ms: int
) -> None:
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=ButtonRequestType.ProtectCall,
)
async def require_confirm_safety_checks(
ctx: wire.GenericContext, level: SafetyCheckLevel
) -> None:
if level == SafetyCheckLevel.PromptAlways:
await confirm_action(
ctx,
"set_safety_checks",
"Safety override",
hold=True,
verb="Hold to confirm",
description="Trezor will allow you to approve some actions which might be unsafe.",
action="Are you sure?",
reverse=True,
larger_vspace=True,
br_code=ButtonRequestType.ProtectCall,
)
elif level == SafetyCheckLevel.PromptTemporarily:
await confirm_action(
ctx,
"set_safety_checks",
"Safety override",
hold=True,
verb="Hold to confirm",
description="Trezor will temporarily allow you to approve some actions which might be unsafe.",
action="Are you sure?",
reverse=True,
br_code=ButtonRequestType.ProtectCall,
)
elif 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=ButtonRequestType.ProtectCall,
)
else:
raise ValueError # enum value out of range
async def require_confirm_experimental_features(
ctx: wire.GenericContext, enable: bool
) -> None:
if enable:
await confirm_action(
ctx,
"set_experimental_features",
"Experimental mode",
description="Enable experimental features?",
action="Only for development and beta testing!",
reverse=True,
br_code=ButtonRequestType.ProtectCall,
)