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.
204 lines
7.7 KiB
204 lines
7.7 KiB
import storage.device
|
|
from trezor import ui, wire, workflow
|
|
from trezor.messages import ButtonRequestType, SafetyCheckLevel
|
|
from trezor.messages.Success import Success
|
|
from trezor.strings import format_duration_ms
|
|
from trezor.ui.text import Text
|
|
|
|
from apps.base import lock_device
|
|
from apps.common import safety_checks
|
|
from apps.common.confirm import require_confirm, require_hold_to_confirm
|
|
|
|
if False:
|
|
from trezor.messages.ApplySettings import ApplySettings, EnumTypeSafetyCheckLevel
|
|
|
|
|
|
def validate_homescreen(homescreen: bytes) -> None:
|
|
if homescreen == b"":
|
|
return
|
|
|
|
if len(homescreen) > storage.device.HOMESCREEN_MAXSIZE:
|
|
raise wire.DataError(
|
|
"Homescreen is too large, maximum size is {} bytes".format(
|
|
storage.device.HOMESCREEN_MAXSIZE
|
|
)
|
|
)
|
|
|
|
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):
|
|
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:
|
|
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")
|
|
|
|
|
|
def reload_settings_from_storage() -> None:
|
|
workflow.idle_timer.set(storage.device.get_autolock_delay_ms(), lock_device)
|
|
ui.display.orientation(storage.device.get_rotation())
|
|
wire.experimental_enabled = storage.device.get_experimental_features()
|
|
|
|
|
|
async def require_confirm_change_homescreen(ctx):
|
|
text = Text("Set homescreen", ui.ICON_CONFIG)
|
|
text.normal("Do you really want to", "change the homescreen", "image?")
|
|
await require_confirm(ctx, text, ButtonRequestType.ProtectCall)
|
|
|
|
|
|
async def require_confirm_change_label(ctx, label):
|
|
text = Text("Change label", ui.ICON_CONFIG)
|
|
text.normal("Do you really want to", "change the label to")
|
|
text.bold("%s?" % label)
|
|
await require_confirm(ctx, text, ButtonRequestType.ProtectCall)
|
|
|
|
|
|
async def require_confirm_change_passphrase(ctx, use):
|
|
text = Text("Enable passphrase" if use else "Disable passphrase", ui.ICON_CONFIG)
|
|
text.normal("Do you really want to")
|
|
text.normal("enable passphrase" if use else "disable passphrase")
|
|
text.normal("encryption?")
|
|
await require_confirm(ctx, text, ButtonRequestType.ProtectCall)
|
|
|
|
|
|
async def require_confirm_change_passphrase_source(
|
|
ctx, passphrase_always_on_device: bool
|
|
):
|
|
text = Text("Passphrase source", ui.ICON_CONFIG)
|
|
if passphrase_always_on_device:
|
|
text.normal(
|
|
"Do you really want to", "enter passphrase always", "on the device?"
|
|
)
|
|
else:
|
|
text.normal("Do you want to revoke", "the passphrase on device", "setting?")
|
|
await require_confirm(ctx, text, ButtonRequestType.ProtectCall)
|
|
|
|
|
|
async def require_confirm_change_display_rotation(ctx, rotation):
|
|
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")
|
|
text = Text("Change rotation", ui.ICON_CONFIG, new_lines=False)
|
|
text.normal("Do you really want to", "change display rotation")
|
|
text.normal("to")
|
|
text.bold("%s?" % label)
|
|
await require_confirm(ctx, text, ButtonRequestType.ProtectCall)
|
|
|
|
|
|
async def require_confirm_change_autolock_delay(ctx, delay_ms):
|
|
text = Text("Auto-lock delay", ui.ICON_CONFIG, new_lines=False)
|
|
text.normal("Do you really want to", "auto-lock your device", "after")
|
|
text.bold("{}?".format(format_duration_ms(delay_ms)))
|
|
await require_confirm(ctx, text, ButtonRequestType.ProtectCall)
|
|
|
|
|
|
async def require_confirm_safety_checks(ctx, level: EnumTypeSafetyCheckLevel) -> None:
|
|
if level == SafetyCheckLevel.PromptAlways:
|
|
text = Text("Safety override", ui.ICON_CONFIG)
|
|
text.normal(
|
|
"Trezor will allow you to",
|
|
"approve some actions",
|
|
"which might be unsafe.",
|
|
)
|
|
text.br_half()
|
|
text.bold("Are you sure?")
|
|
await require_hold_to_confirm(ctx, text, ButtonRequestType.ProtectCall)
|
|
elif level == SafetyCheckLevel.PromptTemporarily:
|
|
text = Text("Safety override", ui.ICON_CONFIG)
|
|
text.normal(
|
|
"Trezor will temporarily",
|
|
"allow you to approve",
|
|
"some actions which",
|
|
"might be unsafe.",
|
|
)
|
|
text.bold("Are you sure?")
|
|
await require_hold_to_confirm(ctx, text, ButtonRequestType.ProtectCall)
|
|
elif level == SafetyCheckLevel.Strict:
|
|
text = Text("Safety checks", ui.ICON_CONFIG)
|
|
text.normal(
|
|
"Do you really want to", "enforce strict safety", "checks (recommended)?"
|
|
)
|
|
await require_confirm(ctx, text, ButtonRequestType.ProtectCall)
|
|
else:
|
|
raise ValueError # enum value out of range
|
|
|
|
|
|
async def require_confirm_experimental_features(ctx, enable: bool) -> None:
|
|
if enable:
|
|
text = Text("Experimental mode", ui.ICON_CONFIG)
|
|
text.normal("Enable experimental", "features?")
|
|
text.br_half()
|
|
text.bold("Only for development", "and beta testing!")
|
|
await require_confirm(ctx, text, ButtonRequestType.ProtectCall)
|