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/reset_device/layout.py

233 lines
6.9 KiB

from micropython import const
from typing import TYPE_CHECKING
from trezor.enums import ButtonRequestType
from trezor.ui.layouts import show_success
from trezor.ui.layouts.reset import ( # noqa: F401
show_share_words,
slip39_advanced_prompt_group_threshold,
slip39_advanced_prompt_number_of_groups,
slip39_prompt_number_of_shares,
slip39_prompt_threshold,
slip39_show_checklist,
)
if TYPE_CHECKING:
from typing import Sequence
from trezor.wire import GenericContext
if __debug__:
from apps import debug
_NUM_OF_CHOICES = const(3)
async def show_internal_entropy(ctx: GenericContext, entropy: bytes) -> None:
from trezor.ui.layouts import confirm_blob
await confirm_blob(
ctx,
"entropy",
"Internal entropy",
entropy,
br_code=ButtonRequestType.ResetDevice,
)
async def _confirm_word(
ctx: GenericContext,
share_index: int | None,
share_words: Sequence[str],
offset: int,
count: int,
group_index: int | None = None,
) -> bool:
from trezor.crypto import random
from trezor.ui.layouts.reset import select_word
# remove duplicates
non_duplicates = list(set(share_words))
# shuffle list
random.shuffle(non_duplicates)
# take top _NUM_OF_CHOICES words
choices = non_duplicates[:_NUM_OF_CHOICES]
# select first of them
checked_word = choices[0]
# find its index
checked_index = share_words.index(checked_word) + offset
# shuffle again so the confirmed word is not always the first choice
random.shuffle(choices)
if __debug__:
debug.reset_word_index.publish(checked_index)
# let the user pick a word
selected_word: str = await select_word(
ctx, choices, share_index, checked_index, count, group_index
)
# confirm it is the correct one
return selected_word == checked_word
async def _share_words_confirmed(
ctx: GenericContext,
share_index: int | None,
share_words: Sequence[str],
num_of_shares: int | None = None,
group_index: int | None = None,
) -> bool:
"""Shows initial dialog asking the user to select words, then presents
word selectors. Shows success popup if the user is done, failure if the confirmation
went wrong.
Return true if the words are confirmed successfully.
"""
# TODO: confirm_action("Select the words bla bla")
if await _do_confirm_share_words(ctx, share_index, share_words, group_index):
await _show_confirmation_success(
ctx,
share_index,
num_of_shares,
group_index,
)
return True
else:
await _show_confirmation_failure(ctx)
return False
async def _do_confirm_share_words(
ctx: GenericContext,
share_index: int | None,
share_words: Sequence[str],
group_index: int | None = None,
) -> bool:
from trezor import utils
# divide list into thirds, rounding up, so that chunking by `third` always yields
# three parts (the last one might be shorter)
third = (len(share_words) + 2) // 3
offset = 0
count = len(share_words)
for part in utils.chunks(share_words, third):
if not await _confirm_word(ctx, share_index, part, offset, count, group_index):
return False
offset += len(part)
return True
async def _show_confirmation_success(
ctx: GenericContext,
share_index: int | None = None,
num_of_shares: int | None = None,
group_index: int | None = None,
) -> None:
if share_index is None or num_of_shares is None: # it is a BIP39 backup
subheader = "You have finished verifying your recovery seed."
text = ""
elif share_index == num_of_shares - 1:
if group_index is None:
subheader = "You have finished verifying your recovery shares."
else:
subheader = f"You have finished verifying your recovery shares for group {group_index + 1}."
text = ""
else:
if group_index is None:
subheader = f"Recovery share #{share_index + 1} checked successfully."
text = f"Continue with share #{share_index + 2}."
else:
subheader = f"Group {group_index + 1} - Share {share_index + 1} checked successfully."
text = "Continue with the next share."
return await show_success(ctx, "success_recovery", text, subheader)
async def _show_confirmation_failure(ctx: GenericContext) -> None:
from trezor.ui.layouts import show_warning
await show_warning(
ctx,
"warning_backup_check",
"Please check again.",
"That is the wrong word.",
"Check again",
ButtonRequestType.ResetDevice,
)
async def show_backup_warning(ctx: GenericContext, slip39: bool = False) -> None:
from trezor.ui.layouts.reset import show_warning_backup
await show_warning_backup(ctx, slip39)
async def show_backup_success(ctx: GenericContext) -> None:
text = "Use your backup when you need to recover your wallet."
await show_success(ctx, "success_backup", text, "Your backup is done.")
# BIP39
# ===
async def bip39_show_and_confirm_mnemonic(ctx: GenericContext, mnemonic: str) -> None:
# warn user about mnemonic safety
await show_backup_warning(ctx)
words = mnemonic.split()
while True:
# display paginated mnemonic on the screen
await show_share_words(ctx, words)
# make the user confirm some words from the mnemonic
if await _share_words_confirmed(ctx, None, words):
break # this share is confirmed, go to next one
# SLIP39
# ===
async def slip39_basic_show_and_confirm_shares(
ctx: GenericContext, shares: Sequence[str]
) -> None:
# warn user about mnemonic safety
await show_backup_warning(ctx, True)
for index, share in enumerate(shares):
share_words = share.split(" ")
while True:
# display paginated share on the screen
await show_share_words(ctx, share_words, index)
# make the user confirm words from the share
if await _share_words_confirmed(ctx, index, share_words, len(shares)):
break # this share is confirmed, go to next one
async def slip39_advanced_show_and_confirm_shares(
ctx: GenericContext, shares: Sequence[Sequence[str]]
) -> None:
# warn user about mnemonic safety
await show_backup_warning(ctx, True)
for group_index, group in enumerate(shares):
for share_index, share in enumerate(group):
share_words = share.split(" ")
while True:
# display paginated share on the screen
await show_share_words(ctx, share_words, share_index, group_index)
# make the user confirm words from the share
if await _share_words_confirmed(
ctx, share_index, share_words, len(group), group_index
):
break # this share is confirmed, go to next one