mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-21 22:08:08 +00:00
feat(core): implement repeated backup
This commit is contained in:
parent
f837db048b
commit
8ef7dfab0d
@ -91,8 +91,38 @@ message Features {
|
|||||||
// optional bytes fw_vendor_keys = 26; // obsoleted, use fw_vendor
|
// optional bytes fw_vendor_keys = 26; // obsoleted, use fw_vendor
|
||||||
optional bool unfinished_backup = 27; // report unfinished backup (equals to Storage.unfinished_backup)
|
optional bool unfinished_backup = 27; // report unfinished backup (equals to Storage.unfinished_backup)
|
||||||
optional bool no_backup = 28; // report no backup (equals to Storage.no_backup)
|
optional bool no_backup = 28; // report no backup (equals to Storage.no_backup)
|
||||||
optional bool recovery_mode = 29; // is recovery mode in progress
|
optional RecoveryStatus recovery_status = 29; // whether or not we are in recovery mode and of what kind
|
||||||
repeated Capability capabilities = 30; // list of supported capabilities
|
repeated Capability capabilities = 30; // list of supported capabilities
|
||||||
|
optional BackupType backup_type = 31; // type of device backup (BIP-39 / SLIP-39 basic / SLIP-39 advanced)
|
||||||
|
optional bool sd_card_present = 32; // is SD card present
|
||||||
|
optional bool sd_protection = 33; // is SD Protect enabled
|
||||||
|
optional bool wipe_code_protection = 34; // is wipe code protection enabled
|
||||||
|
optional bytes session_id = 35;
|
||||||
|
optional bool passphrase_always_on_device = 36; // device enforces passphrase entry on Trezor
|
||||||
|
optional SafetyCheckLevel safety_checks = 37; // safety check level, set to Prompt to limit path namespace enforcement
|
||||||
|
optional uint32 auto_lock_delay_ms = 38; // number of milliseconds after which the device locks itself
|
||||||
|
optional uint32 display_rotation = 39; // in degrees from North
|
||||||
|
optional bool experimental_features = 40; // are experimental message types enabled?
|
||||||
|
optional bool busy = 41; // is the device busy, showing "Do not disconnect"?
|
||||||
|
optional HomescreenFormat homescreen_format = 42; // format of the homescreen, 1 = TOIf, 2 = jpg, 3 = TOIG
|
||||||
|
optional bool hide_passphrase_from_host = 43; // should we hide the passphrase when it comes from host?
|
||||||
|
optional string internal_model = 44; // internal model name
|
||||||
|
optional uint32 unit_color = 45; // color of the unit/device
|
||||||
|
optional bool unit_btconly = 46; // unit/device is intended as bitcoin only
|
||||||
|
optional uint32 homescreen_width = 47; // homescreen width in pixels
|
||||||
|
optional uint32 homescreen_height = 48; // homescreen height in pixels
|
||||||
|
optional bool bootloader_locked = 49; // bootloader is locked
|
||||||
|
optional bool language_version_matches = 50 [default=true]; // translation blob version matches firmware version
|
||||||
|
optional uint32 unit_packaging = 51; // unit/device packaging version
|
||||||
|
optional bool haptic_feedback = 52; // haptic feedback is enabled
|
||||||
|
|
||||||
|
enum RecoveryStatus {
|
||||||
|
NoRecovery = 0;
|
||||||
|
InNormalRecovery = 1;
|
||||||
|
InDryRunRecovery = 2;
|
||||||
|
InUnlockRepeatedBackupRecovery = 3;
|
||||||
|
}
|
||||||
|
|
||||||
enum Capability {
|
enum Capability {
|
||||||
option (has_bitcoin_only_values) = true;
|
option (has_bitcoin_only_values) = true;
|
||||||
|
|
||||||
@ -118,28 +148,6 @@ message Features {
|
|||||||
Capability_Brightness = 20 [(bitcoin_only) = true];
|
Capability_Brightness = 20 [(bitcoin_only) = true];
|
||||||
Capability_Haptic = 21 [(bitcoin_only) = true];
|
Capability_Haptic = 21 [(bitcoin_only) = true];
|
||||||
}
|
}
|
||||||
optional BackupType backup_type = 31; // type of device backup (BIP-39 / SLIP-39 basic / SLIP-39 advanced)
|
|
||||||
optional bool sd_card_present = 32; // is SD card present
|
|
||||||
optional bool sd_protection = 33; // is SD Protect enabled
|
|
||||||
optional bool wipe_code_protection = 34; // is wipe code protection enabled
|
|
||||||
optional bytes session_id = 35;
|
|
||||||
optional bool passphrase_always_on_device = 36; // device enforces passphrase entry on Trezor
|
|
||||||
optional SafetyCheckLevel safety_checks = 37; // safety check level, set to Prompt to limit path namespace enforcement
|
|
||||||
optional uint32 auto_lock_delay_ms = 38; // number of milliseconds after which the device locks itself
|
|
||||||
optional uint32 display_rotation = 39; // in degrees from North
|
|
||||||
optional bool experimental_features = 40; // are experimental message types enabled?
|
|
||||||
optional bool busy = 41; // is the device busy, showing "Do not disconnect"?
|
|
||||||
optional HomescreenFormat homescreen_format = 42; // format of the homescreen, 1 = TOIf, 2 = jpg, 3 = TOIG
|
|
||||||
optional bool hide_passphrase_from_host = 43; // should we hide the passphrase when it comes from host?
|
|
||||||
optional string internal_model = 44; // internal model name
|
|
||||||
optional uint32 unit_color = 45; // color of the unit/device
|
|
||||||
optional bool unit_btconly = 46; // unit/device is intended as bitcoin only
|
|
||||||
optional uint32 homescreen_width = 47; // homescreen width in pixels
|
|
||||||
optional uint32 homescreen_height = 48; // homescreen height in pixels
|
|
||||||
optional bool bootloader_locked = 49; // bootloader is locked
|
|
||||||
optional bool language_version_matches = 50 [default=true]; // translation blob version matches firmware version
|
|
||||||
optional uint32 unit_packaging = 51; // unit/device packaging version
|
|
||||||
optional bool haptic_feedback = 52; // haptic feedback is enabled
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -434,16 +442,16 @@ message EntropyAck {
|
|||||||
* @next WordRequest
|
* @next WordRequest
|
||||||
*/
|
*/
|
||||||
message RecoveryDevice {
|
message RecoveryDevice {
|
||||||
optional uint32 word_count = 1; // number of words in BIP-39 mnemonic
|
optional uint32 word_count = 1; // number of words in BIP-39 mnemonic (T1 only)
|
||||||
optional bool passphrase_protection = 2; // enable master node encryption using passphrase
|
optional bool passphrase_protection = 2; // enable master node encryption using passphrase
|
||||||
optional bool pin_protection = 3; // enable PIN protection
|
optional bool pin_protection = 3; // enable PIN protection
|
||||||
optional string language = 4 [deprecated=true]; // deprecated (use ChangeLanguage)
|
optional string language = 4 [deprecated=true]; // deprecated (use ChangeLanguage)
|
||||||
optional string label = 5; // device label
|
optional string label = 5; // device label
|
||||||
optional bool enforce_wordlist = 6; // enforce BIP-39 wordlist during the process
|
optional bool enforce_wordlist = 6; // enforce BIP-39 wordlist during the process (T1 only)
|
||||||
// 7 reserved for unused recovery method
|
reserved 7; // unused recovery method
|
||||||
optional RecoveryDeviceType type = 8; // supported recovery type
|
optional RecoveryDeviceType type = 8; // supported recovery type (T1 only)
|
||||||
optional uint32 u2f_counter = 9; // U2F counter
|
optional uint32 u2f_counter = 9; // U2F counter
|
||||||
optional bool dry_run = 10; // perform dry-run recovery workflow (for safe mnemonic validation)
|
optional RecoveryKind kind = 10 [default=NormalRecovery]; // the kind of recovery to perform
|
||||||
/**
|
/**
|
||||||
* Type of recovery procedure. These should be used as bitmask, e.g.,
|
* Type of recovery procedure. These should be used as bitmask, e.g.,
|
||||||
* `RecoveryDeviceType_ScrambledWords | RecoveryDeviceType_Matrix`
|
* `RecoveryDeviceType_ScrambledWords | RecoveryDeviceType_Matrix`
|
||||||
@ -457,6 +465,12 @@ message RecoveryDevice {
|
|||||||
RecoveryDeviceType_ScrambledWords = 0; // words in scrambled order
|
RecoveryDeviceType_ScrambledWords = 0; // words in scrambled order
|
||||||
RecoveryDeviceType_Matrix = 1; // matrix recovery type
|
RecoveryDeviceType_Matrix = 1; // matrix recovery type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum RecoveryKind {
|
||||||
|
NormalRecovery = 0; // recovery from seedphrase on an uninitialized device
|
||||||
|
DryRun = 1; // mnemonic validation
|
||||||
|
UnlockRepeatedBackup = 2; // unlock SLIP-39 repeated backup
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
1
core/.changelog.d/3640.added
Normal file
1
core/.changelog.d/3640.added
Normal file
@ -0,0 +1 @@
|
|||||||
|
Added support for repeated backups.
|
@ -293,6 +293,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_joint__title;
|
MP_QSTR_joint__title;
|
||||||
MP_QSTR_joint__to_the_total_amount;
|
MP_QSTR_joint__to_the_total_amount;
|
||||||
MP_QSTR_joint__you_are_contributing;
|
MP_QSTR_joint__you_are_contributing;
|
||||||
|
MP_QSTR_kind;
|
||||||
MP_QSTR_label;
|
MP_QSTR_label;
|
||||||
MP_QSTR_language;
|
MP_QSTR_language;
|
||||||
MP_QSTR_language__change_to_template;
|
MP_QSTR_language__change_to_template;
|
||||||
@ -440,7 +441,10 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_recovery__title_dry_run;
|
MP_QSTR_recovery__title_dry_run;
|
||||||
MP_QSTR_recovery__title_recover;
|
MP_QSTR_recovery__title_recover;
|
||||||
MP_QSTR_recovery__title_remaining_shares;
|
MP_QSTR_recovery__title_remaining_shares;
|
||||||
|
MP_QSTR_recovery__title_unlock_repeated_backup;
|
||||||
MP_QSTR_recovery__type_word_x_of_y_template;
|
MP_QSTR_recovery__type_word_x_of_y_template;
|
||||||
|
MP_QSTR_recovery__unlock_repeated_backup;
|
||||||
|
MP_QSTR_recovery__unlock_repeated_backup_verb;
|
||||||
MP_QSTR_recovery__wallet_recovered;
|
MP_QSTR_recovery__wallet_recovered;
|
||||||
MP_QSTR_recovery__wanna_cancel_dry_run;
|
MP_QSTR_recovery__wanna_cancel_dry_run;
|
||||||
MP_QSTR_recovery__wanna_cancel_recovery;
|
MP_QSTR_recovery__wanna_cancel_recovery;
|
||||||
|
@ -798,7 +798,7 @@ pub enum TranslatedString {
|
|||||||
recovery__num_of_words = 511, // "Select the number of words in your backup."
|
recovery__num_of_words = 511, // "Select the number of words in your backup."
|
||||||
recovery__only_first_n_letters = 512, // "You'll only have to select the first 2-4 letters of each word."
|
recovery__only_first_n_letters = 512, // "You'll only have to select the first 2-4 letters of each word."
|
||||||
recovery__progress_will_be_lost = 513, // "All progress will be lost."
|
recovery__progress_will_be_lost = 513, // "All progress will be lost."
|
||||||
recovery__select_num_of_words = 514, // "Select the number of words in your backup."
|
recovery__select_num_of_words = 514, // "\"\""
|
||||||
recovery__share_already_entered = 515, // "Share already entered"
|
recovery__share_already_entered = 515, // "Share already entered"
|
||||||
recovery__share_from_another_multi_share_backup = 516, // "You have entered a share from a different backup."
|
recovery__share_from_another_multi_share_backup = 516, // "You have entered a share from a different backup."
|
||||||
recovery__share_num_template = 517, // "Share {0}"
|
recovery__share_num_template = 517, // "Share {0}"
|
||||||
@ -1330,6 +1330,9 @@ pub enum TranslatedString {
|
|||||||
words__try_again = 930, // "Try again."
|
words__try_again = 930, // "Try again."
|
||||||
reset__slip39_checklist_num_groups_x_template = 931, // "Number of groups: {0}"
|
reset__slip39_checklist_num_groups_x_template = 931, // "Number of groups: {0}"
|
||||||
brightness__title = 932, // "Set brightness"
|
brightness__title = 932, // "Set brightness"
|
||||||
|
recovery__title_unlock_repeated_backup = 933, // "Multi-share backup"
|
||||||
|
recovery__unlock_repeated_backup = 934, // "Do you want to unlock the seed for repeated backup?"
|
||||||
|
recovery__unlock_repeated_backup_verb = 935, // "Unlock backup"
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TranslatedString {
|
impl TranslatedString {
|
||||||
@ -2123,7 +2126,7 @@ impl TranslatedString {
|
|||||||
Self::recovery__num_of_words => "Select the number of words in your backup.",
|
Self::recovery__num_of_words => "Select the number of words in your backup.",
|
||||||
Self::recovery__only_first_n_letters => "You'll only have to select the first 2-4 letters of each word.",
|
Self::recovery__only_first_n_letters => "You'll only have to select the first 2-4 letters of each word.",
|
||||||
Self::recovery__progress_will_be_lost => "All progress will be lost.",
|
Self::recovery__progress_will_be_lost => "All progress will be lost.",
|
||||||
Self::recovery__select_num_of_words => "Select the number of words in your backup.",
|
Self::recovery__select_num_of_words => "\"\"",
|
||||||
Self::recovery__share_already_entered => "Share already entered",
|
Self::recovery__share_already_entered => "Share already entered",
|
||||||
Self::recovery__share_from_another_multi_share_backup => "You have entered a share from a different backup.",
|
Self::recovery__share_from_another_multi_share_backup => "You have entered a share from a different backup.",
|
||||||
Self::recovery__share_num_template => "Share {0}",
|
Self::recovery__share_num_template => "Share {0}",
|
||||||
@ -2655,6 +2658,9 @@ impl TranslatedString {
|
|||||||
Self::words__try_again => "Try again.",
|
Self::words__try_again => "Try again.",
|
||||||
Self::reset__slip39_checklist_num_groups_x_template => "Number of groups: {0}",
|
Self::reset__slip39_checklist_num_groups_x_template => "Number of groups: {0}",
|
||||||
Self::brightness__title => "Set brightness",
|
Self::brightness__title => "Set brightness",
|
||||||
|
Self::recovery__title_unlock_repeated_backup => "Multi-share backup",
|
||||||
|
Self::recovery__unlock_repeated_backup => "Do you want to unlock the seed for repeated backup?",
|
||||||
|
Self::recovery__unlock_repeated_backup_verb => "Unlock backup",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3981,6 +3987,9 @@ impl TranslatedString {
|
|||||||
Qstr::MP_QSTR_words__try_again => Some(Self::words__try_again),
|
Qstr::MP_QSTR_words__try_again => Some(Self::words__try_again),
|
||||||
Qstr::MP_QSTR_reset__slip39_checklist_num_groups_x_template => Some(Self::reset__slip39_checklist_num_groups_x_template),
|
Qstr::MP_QSTR_reset__slip39_checklist_num_groups_x_template => Some(Self::reset__slip39_checklist_num_groups_x_template),
|
||||||
Qstr::MP_QSTR_brightness__title => Some(Self::brightness__title),
|
Qstr::MP_QSTR_brightness__title => Some(Self::brightness__title),
|
||||||
|
Qstr::MP_QSTR_recovery__title_unlock_repeated_backup => Some(Self::recovery__title_unlock_repeated_backup),
|
||||||
|
Qstr::MP_QSTR_recovery__unlock_repeated_backup => Some(Self::recovery__unlock_repeated_backup),
|
||||||
|
Qstr::MP_QSTR_recovery__unlock_repeated_backup_verb => Some(Self::recovery__unlock_repeated_backup_verb),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,6 +228,9 @@ impl ComponentMsgObj for super::component::bl_confirm::Confirm<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RECOVERY_KIND_DRY_RUN: u32 = 1;
|
||||||
|
const RECOVERY_KIND_UNLOCK_REPEATED_BACKUP: u32 = 2;
|
||||||
|
|
||||||
/// Function to create and call a `ButtonPage` dialog based on paginable content
|
/// Function to create and call a `ButtonPage` dialog based on paginable content
|
||||||
/// (e.g. `Paragraphs` or `FormattedText`).
|
/// (e.g. `Paragraphs` or `FormattedText`).
|
||||||
/// Has optional title (supply empty `TString` for that) and hold-to-confirm
|
/// Has optional title (supply empty `TString` for that) and hold-to-confirm
|
||||||
@ -1395,7 +1398,7 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
||||||
let button: TString<'static> = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
|
let button: TString<'static> = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
|
||||||
let dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?;
|
let kind: u32 = kwargs.get(Qstr::MP_QSTR_kind)?.try_into()?;
|
||||||
let show_info: bool = kwargs.get(Qstr::MP_QSTR_show_info)?.try_into()?;
|
let show_info: bool = kwargs.get(Qstr::MP_QSTR_show_info)?.try_into()?;
|
||||||
|
|
||||||
let mut paragraphs = ParagraphVecShort::new();
|
let mut paragraphs = ParagraphVecShort::new();
|
||||||
@ -1412,10 +1415,10 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = if dry_run {
|
let title = match kind {
|
||||||
TR::recovery__title_dry_run
|
RECOVERY_KIND_DRY_RUN => TR::recovery__title_dry_run,
|
||||||
} else {
|
RECOVERY_KIND_UNLOCK_REPEATED_BACKUP => TR::recovery__title_unlock_repeated_backup,
|
||||||
TR::recovery__title
|
_ => TR::recovery__title,
|
||||||
};
|
};
|
||||||
|
|
||||||
content_in_button_page(
|
content_in_button_page(
|
||||||
@ -1965,7 +1968,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// title: str, # unused on TR
|
/// title: str, # unused on TR
|
||||||
/// description: str,
|
/// description: str,
|
||||||
/// button: str,
|
/// button: str,
|
||||||
/// dry_run: bool,
|
/// kind: int, # RecoveryKind enum, passed as an int
|
||||||
/// info_button: bool, # unused on TR
|
/// info_button: bool, # unused on TR
|
||||||
/// show_info: bool,
|
/// show_info: bool,
|
||||||
/// ) -> LayoutObj[UiResult]:
|
/// ) -> LayoutObj[UiResult]:
|
||||||
|
@ -310,6 +310,9 @@ impl ComponentMsgObj for super::component::bl_confirm::Confirm<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RECOVERY_KIND_DRY_RUN: u32 = 1;
|
||||||
|
const RECOVERY_KIND_UNLOCK_REPEATED_BACKUP: u32 = 2;
|
||||||
|
|
||||||
extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
@ -1365,7 +1368,7 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
||||||
let button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
|
let button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
|
||||||
let dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?;
|
let kind: u32 = kwargs.get(Qstr::MP_QSTR_kind)?.try_into()?;
|
||||||
let info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false)?;
|
let info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false)?;
|
||||||
|
|
||||||
let paragraphs = Paragraphs::new([
|
let paragraphs = Paragraphs::new([
|
||||||
@ -1374,10 +1377,12 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
])
|
])
|
||||||
.with_spacing(theme::RECOVERY_SPACING);
|
.with_spacing(theme::RECOVERY_SPACING);
|
||||||
|
|
||||||
let notification: TString = if dry_run {
|
let notification = match kind {
|
||||||
TR::recovery__title_dry_run.into()
|
RECOVERY_KIND_DRY_RUN => TR::recovery__title_dry_run.into(),
|
||||||
} else {
|
RECOVERY_KIND_UNLOCK_REPEATED_BACKUP => {
|
||||||
TR::recovery__title.into()
|
TR::recovery__title_unlock_repeated_backup.into()
|
||||||
|
}
|
||||||
|
_ => TR::recovery__title.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let obj = if info_button {
|
let obj = if info_button {
|
||||||
@ -1415,7 +1420,7 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu
|
|||||||
|
|
||||||
let paragraphs = Paragraphs::new(Paragraph::new(
|
let paragraphs = Paragraphs::new(Paragraph::new(
|
||||||
&theme::TEXT_DEMIBOLD,
|
&theme::TEXT_DEMIBOLD,
|
||||||
TR::recovery__select_num_of_words,
|
TR::recovery__num_of_words,
|
||||||
));
|
));
|
||||||
|
|
||||||
let obj = LayoutObj::new(Frame::left_aligned(
|
let obj = LayoutObj::new(Frame::left_aligned(
|
||||||
@ -2042,7 +2047,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// title: str,
|
/// title: str,
|
||||||
/// description: str,
|
/// description: str,
|
||||||
/// button: str,
|
/// button: str,
|
||||||
/// dry_run: bool,
|
/// kind: int, # RecoveryKind enum, passed as an int
|
||||||
/// info_button: bool = False,
|
/// info_button: bool = False,
|
||||||
/// ) -> LayoutObj[UiResult]:
|
/// ) -> LayoutObj[UiResult]:
|
||||||
/// """Device recovery homescreen."""
|
/// """Device recovery homescreen."""
|
||||||
|
@ -980,7 +980,7 @@ def confirm_recovery(
|
|||||||
title: str, # unused on TR
|
title: str, # unused on TR
|
||||||
description: str,
|
description: str,
|
||||||
button: str,
|
button: str,
|
||||||
dry_run: bool,
|
kind: int, # RecoveryKind enum, passed as an int
|
||||||
info_button: bool, # unused on TR
|
info_button: bool, # unused on TR
|
||||||
show_info: bool,
|
show_info: bool,
|
||||||
) -> LayoutObj[UiResult]:
|
) -> LayoutObj[UiResult]:
|
||||||
@ -1515,7 +1515,7 @@ def confirm_recovery(
|
|||||||
title: str,
|
title: str,
|
||||||
description: str,
|
description: str,
|
||||||
button: str,
|
button: str,
|
||||||
dry_run: bool,
|
kind: int, # RecoveryKind enum, passed as an int
|
||||||
info_button: bool = False,
|
info_button: bool = False,
|
||||||
) -> LayoutObj[UiResult]:
|
) -> LayoutObj[UiResult]:
|
||||||
"""Device recovery homescreen."""
|
"""Device recovery homescreen."""
|
||||||
|
@ -573,7 +573,6 @@ class TR:
|
|||||||
recovery__num_of_words: str = "Select the number of words in your backup."
|
recovery__num_of_words: str = "Select the number of words in your backup."
|
||||||
recovery__only_first_n_letters: str = "You'll only have to select the first 2-4 letters of each word."
|
recovery__only_first_n_letters: str = "You'll only have to select the first 2-4 letters of each word."
|
||||||
recovery__progress_will_be_lost: str = "All progress will be lost."
|
recovery__progress_will_be_lost: str = "All progress will be lost."
|
||||||
recovery__select_num_of_words: str = "Select the number of words in your backup."
|
|
||||||
recovery__share_already_entered: str = "Share already entered"
|
recovery__share_already_entered: str = "Share already entered"
|
||||||
recovery__share_does_not_match: str = "Share doesn't match"
|
recovery__share_does_not_match: str = "Share doesn't match"
|
||||||
recovery__share_from_another_multi_share_backup: str = "You have entered a share from a different backup."
|
recovery__share_from_another_multi_share_backup: str = "You have entered a share from a different backup."
|
||||||
@ -584,7 +583,10 @@ class TR:
|
|||||||
recovery__title_dry_run: str = "Backup check"
|
recovery__title_dry_run: str = "Backup check"
|
||||||
recovery__title_recover: str = "Recover wallet"
|
recovery__title_recover: str = "Recover wallet"
|
||||||
recovery__title_remaining_shares: str = "Remaining shares"
|
recovery__title_remaining_shares: str = "Remaining shares"
|
||||||
|
recovery__title_unlock_repeated_backup: str = "Multi-share backup"
|
||||||
recovery__type_word_x_of_y_template: str = "Type word {0} of {1}"
|
recovery__type_word_x_of_y_template: str = "Type word {0} of {1}"
|
||||||
|
recovery__unlock_repeated_backup: str = "Do you want to unlock the seed for repeated backup?"
|
||||||
|
recovery__unlock_repeated_backup_verb: str = "Unlock backup"
|
||||||
recovery__wallet_recovered: str = "Wallet recovery completed"
|
recovery__wallet_recovered: str = "Wallet recovery completed"
|
||||||
recovery__wanna_cancel_dry_run: str = "Are you sure you want to cancel the backup check?"
|
recovery__wanna_cancel_dry_run: str = "Are you sure you want to cancel the backup check?"
|
||||||
recovery__wanna_cancel_recovery: str = "Are you sure you want to cancel the recovery process?"
|
recovery__wanna_cancel_recovery: str = "Are you sure you want to cancel the recovery process?"
|
||||||
|
4
core/src/all_modules.py
generated
4
core/src/all_modules.py
generated
@ -121,6 +121,10 @@ trezor.enums.PinMatrixRequestType
|
|||||||
import trezor.enums.PinMatrixRequestType
|
import trezor.enums.PinMatrixRequestType
|
||||||
trezor.enums.RecoveryDeviceType
|
trezor.enums.RecoveryDeviceType
|
||||||
import trezor.enums.RecoveryDeviceType
|
import trezor.enums.RecoveryDeviceType
|
||||||
|
trezor.enums.RecoveryKind
|
||||||
|
import trezor.enums.RecoveryKind
|
||||||
|
trezor.enums.RecoveryStatus
|
||||||
|
import trezor.enums.RecoveryStatus
|
||||||
trezor.enums.RequestType
|
trezor.enums.RequestType
|
||||||
import trezor.enums.RequestType
|
import trezor.enums.RequestType
|
||||||
trezor.enums.SafetyCheckLevel
|
trezor.enums.SafetyCheckLevel
|
||||||
|
@ -60,7 +60,7 @@ def _language_version_matches() -> bool | None:
|
|||||||
def get_features() -> Features:
|
def get_features() -> Features:
|
||||||
import storage.recovery as storage_recovery
|
import storage.recovery as storage_recovery
|
||||||
from trezor import translations
|
from trezor import translations
|
||||||
from trezor.enums import Capability
|
from trezor.enums import Capability, RecoveryKind, RecoveryStatus
|
||||||
from trezor.messages import Features
|
from trezor.messages import Features
|
||||||
from trezor.ui import HEIGHT, WIDTH
|
from trezor.ui import HEIGHT, WIDTH
|
||||||
|
|
||||||
@ -160,7 +160,16 @@ def get_features() -> Features:
|
|||||||
f.unfinished_backup = storage_device.unfinished_backup()
|
f.unfinished_backup = storage_device.unfinished_backup()
|
||||||
f.no_backup = storage_device.no_backup()
|
f.no_backup = storage_device.no_backup()
|
||||||
f.flags = storage_device.get_flags()
|
f.flags = storage_device.get_flags()
|
||||||
f.recovery_mode = storage_recovery.is_in_progress()
|
if storage_recovery.is_in_progress():
|
||||||
|
kind = storage_recovery.get_kind()
|
||||||
|
if kind == RecoveryKind.NormalRecovery:
|
||||||
|
f.recovery_status = RecoveryStatus.InNormalRecovery
|
||||||
|
elif kind == RecoveryKind.DryRun:
|
||||||
|
f.recovery_status = RecoveryStatus.InDryRunRecovery
|
||||||
|
elif kind == RecoveryKind.UnlockRepeatedBackup:
|
||||||
|
f.recovery_status = RecoveryStatus.InUnlockRepeatedBackupRecovery
|
||||||
|
else:
|
||||||
|
f.recovery_status = RecoveryStatus.NoRecovery
|
||||||
f.backup_type = mnemonic.get_type()
|
f.backup_type = mnemonic.get_type()
|
||||||
|
|
||||||
# Only some models are capable of SD card
|
# Only some models are capable of SD card
|
||||||
@ -216,6 +225,7 @@ async def handle_GetFeatures(msg: GetFeatures) -> Features:
|
|||||||
|
|
||||||
|
|
||||||
async def handle_Cancel(msg: Cancel) -> Success:
|
async def handle_Cancel(msg: Cancel) -> Success:
|
||||||
|
workflow.close_others()
|
||||||
raise wire.ActionCancelled
|
raise wire.ActionCancelled
|
||||||
|
|
||||||
|
|
||||||
@ -370,7 +380,7 @@ def set_homescreen() -> None:
|
|||||||
def lock_device(interrupt_workflow: bool = True) -> None:
|
def lock_device(interrupt_workflow: bool = True) -> None:
|
||||||
if config.has_pin():
|
if config.has_pin():
|
||||||
config.lock()
|
config.lock()
|
||||||
wire.find_handler = get_pinlocked_handler
|
wire.find_handler = _get_pinlocked_handler
|
||||||
set_homescreen()
|
set_homescreen()
|
||||||
if interrupt_workflow:
|
if interrupt_workflow:
|
||||||
workflow.close_others()
|
workflow.close_others()
|
||||||
@ -409,7 +419,7 @@ async def unlock_device() -> None:
|
|||||||
wire.find_handler = workflow_handlers.find_registered_handler
|
wire.find_handler = workflow_handlers.find_registered_handler
|
||||||
|
|
||||||
|
|
||||||
def get_pinlocked_handler(
|
def _get_pinlocked_handler(
|
||||||
iface: wire.WireInterface, msg_type: int
|
iface: wire.WireInterface, msg_type: int
|
||||||
) -> wire.Handler[wire.Msg] | None:
|
) -> wire.Handler[wire.Msg] | None:
|
||||||
orig_handler = workflow_handlers.find_registered_handler(iface, msg_type)
|
orig_handler = workflow_handlers.find_registered_handler(iface, msg_type)
|
||||||
@ -432,6 +442,38 @@ def get_pinlocked_handler(
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
_ALLOW_WHILE_REPEATED_BACKUP_UNLOCKED = (
|
||||||
|
MessageType.Initialize,
|
||||||
|
MessageType.GetFeatures,
|
||||||
|
MessageType.EndSession,
|
||||||
|
MessageType.BackupDevice,
|
||||||
|
MessageType.WipeDevice,
|
||||||
|
MessageType.Cancel,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_backup_handler(
|
||||||
|
iface: wire.WireInterface, msg_type: int
|
||||||
|
) -> wire.Handler[wire.Msg] | None:
|
||||||
|
orig_handler = workflow_handlers.find_registered_handler(iface, msg_type)
|
||||||
|
if orig_handler is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if __debug__:
|
||||||
|
import usb
|
||||||
|
|
||||||
|
if iface is usb.iface_debug:
|
||||||
|
return orig_handler
|
||||||
|
|
||||||
|
if msg_type in _ALLOW_WHILE_REPEATED_BACKUP_UNLOCKED:
|
||||||
|
return orig_handler
|
||||||
|
|
||||||
|
async def wrapper(_msg: protobuf.MessageType) -> protobuf.MessageType:
|
||||||
|
raise wire.ProcessError("Operation not allowed when in repeated backup state")
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
# this function is also called when handling ApplySettings
|
# this function is also called when handling ApplySettings
|
||||||
def reload_settings_from_storage() -> None:
|
def reload_settings_from_storage() -> None:
|
||||||
from trezor import ui
|
from trezor import ui
|
||||||
@ -464,7 +506,9 @@ def boot() -> None:
|
|||||||
workflow_handlers.register(msg_type, handler)
|
workflow_handlers.register(msg_type, handler)
|
||||||
|
|
||||||
reload_settings_from_storage()
|
reload_settings_from_storage()
|
||||||
if config.is_unlocked():
|
if not config.is_unlocked():
|
||||||
wire.find_handler = workflow_handlers.find_registered_handler
|
wire.find_handler = _get_pinlocked_handler
|
||||||
|
elif storage_cache.get_bool(storage_cache.APP_RECOVERY_REPEATED_BACKUP_UNLOCKED):
|
||||||
|
wire.find_handler = _get_backup_handler
|
||||||
else:
|
else:
|
||||||
wire.find_handler = get_pinlocked_handler
|
wire.find_handler = workflow_handlers.find_registered_handler
|
||||||
|
@ -10,17 +10,25 @@ BAK_T_BIP39 = BackupType.Bip39 # global_import_cache
|
|||||||
|
|
||||||
|
|
||||||
async def backup_device(msg: BackupDevice) -> Success:
|
async def backup_device(msg: BackupDevice) -> Success:
|
||||||
|
import storage.cache as storage_cache
|
||||||
import storage.device as storage_device
|
import storage.device as storage_device
|
||||||
from trezor import wire
|
from trezor import wire
|
||||||
from trezor.messages import Success
|
from trezor.messages import Success
|
||||||
|
|
||||||
|
from apps import workflow_handlers
|
||||||
from apps.common import backup_types, mnemonic
|
from apps.common import backup_types, mnemonic
|
||||||
|
|
||||||
from .reset_device import backup_seed, backup_slip39_custom, layout
|
from .reset_device import backup_seed, backup_slip39_custom, layout
|
||||||
|
|
||||||
|
# do this early before we show any UI
|
||||||
|
# the homescreen will clear the flag right after its own UI is gone
|
||||||
|
repeated_backup_unlocked = storage_cache.get_bool(
|
||||||
|
storage_cache.APP_RECOVERY_REPEATED_BACKUP_UNLOCKED
|
||||||
|
)
|
||||||
|
|
||||||
if not storage_device.is_initialized():
|
if not storage_device.is_initialized():
|
||||||
raise wire.NotInitialized("Device is not initialized")
|
raise wire.NotInitialized("Device is not initialized")
|
||||||
if not storage_device.needs_backup():
|
if not storage_device.needs_backup() and not repeated_backup_unlocked:
|
||||||
raise wire.ProcessError("Seed already backed up")
|
raise wire.ProcessError("Seed already backed up")
|
||||||
|
|
||||||
mnemonic_secret, backup_type = mnemonic.get()
|
mnemonic_secret, backup_type = mnemonic.get()
|
||||||
@ -40,7 +48,10 @@ async def backup_device(msg: BackupDevice) -> Success:
|
|||||||
elif len(groups) > 0:
|
elif len(groups) > 0:
|
||||||
raise wire.DataError("group_threshold is missing")
|
raise wire.DataError("group_threshold is missing")
|
||||||
|
|
||||||
storage_device.set_unfinished_backup(True)
|
if not repeated_backup_unlocked:
|
||||||
|
storage_device.set_unfinished_backup(True)
|
||||||
|
|
||||||
|
storage_cache.delete(storage_cache.APP_RECOVERY_REPEATED_BACKUP_UNLOCKED)
|
||||||
storage_device.set_backed_up()
|
storage_device.set_backed_up()
|
||||||
|
|
||||||
if group_threshold is not None:
|
if group_threshold is not None:
|
||||||
@ -51,6 +62,7 @@ async def backup_device(msg: BackupDevice) -> Success:
|
|||||||
|
|
||||||
storage_device.set_unfinished_backup(False)
|
storage_device.set_unfinished_backup(False)
|
||||||
|
|
||||||
|
wire.find_handler = workflow_handlers.find_registered_handler
|
||||||
await layout.show_backup_success()
|
await layout.show_backup_success()
|
||||||
|
|
||||||
return Success(message="Seed successfully backed up")
|
return Success(message="Seed successfully backed up")
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from trezor.enums import RecoveryKind
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from trezor.messages import RecoveryDevice, Success
|
from trezor.messages import RecoveryDevice, Success
|
||||||
|
|
||||||
# List of RecoveryDevice fields that can be set when doing dry-run recovery.
|
# List of RecoveryDevice fields that can be set when doing dry-run recovery.
|
||||||
# All except `dry_run` are allowed for T1 compatibility, but their values are ignored.
|
# All except `kind` are allowed for T1 compatibility, but their values are ignored.
|
||||||
# If set, `enforce_wordlist` must be True, because we do not support non-enforcing.
|
# If set, `enforce_wordlist` must be True, because we do not support non-enforcing.
|
||||||
DRY_RUN_ALLOWED_FIELDS = ("dry_run", "word_count", "enforce_wordlist", "type")
|
DRY_RUN_ALLOWED_FIELDS = ("kind", "word_count", "enforce_wordlist", "type")
|
||||||
|
|
||||||
|
|
||||||
async def recovery_device(msg: RecoveryDevice) -> Success:
|
async def recovery_device(msg: RecoveryDevice) -> Success:
|
||||||
@ -20,9 +22,10 @@ async def recovery_device(msg: RecoveryDevice) -> Success:
|
|||||||
import storage.device as storage_device
|
import storage.device as storage_device
|
||||||
import storage.recovery as storage_recovery
|
import storage.recovery as storage_recovery
|
||||||
from trezor import TR, config, wire, workflow
|
from trezor import TR, config, wire, workflow
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import BackupType, ButtonRequestType
|
||||||
from trezor.ui.layouts import confirm_action, confirm_reset_device
|
from trezor.ui.layouts import confirm_action, confirm_reset_device
|
||||||
|
|
||||||
|
from apps.common import mnemonic
|
||||||
from apps.common.request_pin import (
|
from apps.common.request_pin import (
|
||||||
error_pin_invalid,
|
error_pin_invalid,
|
||||||
request_pin_and_sd_salt,
|
request_pin_and_sd_salt,
|
||||||
@ -31,68 +34,79 @@ async def recovery_device(msg: RecoveryDevice) -> Success:
|
|||||||
|
|
||||||
from .homescreen import recovery_homescreen, recovery_process
|
from .homescreen import recovery_homescreen, recovery_process
|
||||||
|
|
||||||
dry_run = msg.dry_run # local_cache_attribute
|
recovery_kind = msg.kind # local_cache_attribute
|
||||||
|
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
# validate
|
# validate
|
||||||
if not dry_run and storage_device.is_initialized():
|
if recovery_kind == RecoveryKind.NormalRecovery:
|
||||||
raise wire.UnexpectedMessage("Already initialized")
|
if storage_device.is_initialized():
|
||||||
if dry_run and not storage_device.is_initialized():
|
raise wire.UnexpectedMessage("Already initialized")
|
||||||
raise wire.NotInitialized("Device is not initialized")
|
elif recovery_kind in (RecoveryKind.DryRun, RecoveryKind.UnlockRepeatedBackup):
|
||||||
if msg.enforce_wordlist is False:
|
if not storage_device.is_initialized():
|
||||||
raise wire.ProcessError(
|
raise wire.NotInitialized("Device is not initialized")
|
||||||
"Value enforce_wordlist must be True, Trezor Core enforces words automatically."
|
if (
|
||||||
)
|
recovery_kind == RecoveryKind.UnlockRepeatedBackup
|
||||||
if dry_run:
|
and mnemonic.get_type() == BackupType.Bip39
|
||||||
|
):
|
||||||
|
raise wire.ProcessError("Repeated Backup not available for BIP39 backups")
|
||||||
# check that only allowed fields are set
|
# check that only allowed fields are set
|
||||||
for key, value in msg.__dict__.items():
|
for key, value in msg.__dict__.items():
|
||||||
if key not in DRY_RUN_ALLOWED_FIELDS and value is not None:
|
if key not in DRY_RUN_ALLOWED_FIELDS and value is not None:
|
||||||
raise wire.ProcessError(f"Forbidden field set in dry-run: {key}")
|
raise wire.ProcessError(f"Forbidden field set in dry-run: {key}")
|
||||||
|
else:
|
||||||
|
raise RuntimeError # Unknown RecoveryKind
|
||||||
|
|
||||||
|
if msg.enforce_wordlist is False:
|
||||||
|
raise wire.ProcessError(
|
||||||
|
"Value enforce_wordlist must be True, Trezor Core enforces words automatically."
|
||||||
|
)
|
||||||
# END validate
|
# END validate
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
|
|
||||||
if storage_recovery.is_in_progress():
|
if storage_recovery.is_in_progress():
|
||||||
return await recovery_process()
|
return await recovery_process()
|
||||||
|
|
||||||
# --------------------------------------------------------
|
if recovery_kind == RecoveryKind.NormalRecovery:
|
||||||
# _continue_dialog
|
|
||||||
if not dry_run:
|
|
||||||
await confirm_reset_device(TR.recovery__title_recover, recovery=True)
|
await confirm_reset_device(TR.recovery__title_recover, recovery=True)
|
||||||
else:
|
|
||||||
await confirm_action(
|
|
||||||
"confirm_seedcheck",
|
|
||||||
TR.recovery__title_dry_run,
|
|
||||||
description=TR.recovery__check_dry_run,
|
|
||||||
br_code=ButtonRequestType.ProtectCall,
|
|
||||||
verb=TR.buttons__check,
|
|
||||||
)
|
|
||||||
# END _continue_dialog
|
|
||||||
# --------------------------------------------------------
|
|
||||||
|
|
||||||
if not dry_run:
|
|
||||||
# wipe storage to make sure the device is in a clear state
|
# wipe storage to make sure the device is in a clear state
|
||||||
storage.reset()
|
storage.reset()
|
||||||
|
|
||||||
# for dry run pin needs to be entered
|
|
||||||
if dry_run:
|
|
||||||
curpin, salt = await request_pin_and_sd_salt(TR.pin__enter)
|
|
||||||
if not config.check_pin(curpin, salt):
|
|
||||||
await error_pin_invalid()
|
|
||||||
|
|
||||||
if not dry_run:
|
|
||||||
# set up pin if requested
|
# set up pin if requested
|
||||||
if msg.pin_protection:
|
if msg.pin_protection:
|
||||||
newpin = await request_pin_confirm(allow_cancel=False)
|
newpin = await request_pin_confirm(allow_cancel=False)
|
||||||
config.change_pin("", newpin, None, None)
|
config.change_pin("", newpin, None, None)
|
||||||
|
|
||||||
storage_device.set_passphrase_enabled(bool(msg.passphrase_protection))
|
storage_device.set_passphrase_enabled(bool(msg.passphrase_protection))
|
||||||
|
|
||||||
if msg.u2f_counter is not None:
|
if msg.u2f_counter is not None:
|
||||||
storage_device.set_u2f_counter(msg.u2f_counter)
|
storage_device.set_u2f_counter(msg.u2f_counter)
|
||||||
|
|
||||||
if msg.label is not None:
|
if msg.label is not None:
|
||||||
storage_device.set_label(msg.label)
|
storage_device.set_label(msg.label)
|
||||||
|
|
||||||
|
elif recovery_kind in (RecoveryKind.DryRun, RecoveryKind.UnlockRepeatedBackup):
|
||||||
|
title = (
|
||||||
|
TR.recovery__title_dry_run
|
||||||
|
if recovery_kind == RecoveryKind.DryRun
|
||||||
|
else TR.recovery__title_unlock_repeated_backup
|
||||||
|
)
|
||||||
|
await confirm_action(
|
||||||
|
"confirm_seedcheck",
|
||||||
|
title,
|
||||||
|
description=TR.recovery__check_dry_run,
|
||||||
|
br_code=ButtonRequestType.ProtectCall,
|
||||||
|
verb=TR.buttons__check,
|
||||||
|
)
|
||||||
|
|
||||||
|
curpin, salt = await request_pin_and_sd_salt(TR.pin__enter)
|
||||||
|
if not config.check_pin(curpin, salt):
|
||||||
|
await error_pin_invalid()
|
||||||
|
|
||||||
storage_recovery.set_in_progress(True)
|
storage_recovery.set_in_progress(True)
|
||||||
storage_recovery.set_dry_run(bool(dry_run))
|
|
||||||
|
storage_recovery.set_kind(int(recovery_kind))
|
||||||
|
|
||||||
workflow.set_default(recovery_homescreen)
|
workflow.set_default(recovery_homescreen)
|
||||||
|
|
||||||
return await recovery_process()
|
return await recovery_process()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
import storage.cache as storage_cache
|
||||||
import storage.device as storage_device
|
import storage.device as storage_device
|
||||||
import storage.recovery as storage_recovery
|
import storage.recovery as storage_recovery
|
||||||
import storage.recovery_shares as storage_recovery_shares
|
import storage.recovery_shares as storage_recovery_shares
|
||||||
@ -11,7 +12,7 @@ from apps.common import backup_types
|
|||||||
from . import layout, recover
|
from . import layout, recover
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from trezor.enums import BackupType
|
from trezor.enums import BackupType, RecoveryKind
|
||||||
|
|
||||||
|
|
||||||
async def recovery_homescreen() -> None:
|
async def recovery_homescreen() -> None:
|
||||||
@ -19,35 +20,84 @@ async def recovery_homescreen() -> None:
|
|||||||
|
|
||||||
from apps.homescreen import homescreen
|
from apps.homescreen import homescreen
|
||||||
|
|
||||||
if not storage_recovery.is_in_progress():
|
if storage_cache.get_bool(storage_cache.APP_RECOVERY_REPEATED_BACKUP_UNLOCKED):
|
||||||
|
await _continue_repeated_backup()
|
||||||
|
elif not storage_recovery.is_in_progress():
|
||||||
workflow.set_default(homescreen)
|
workflow.set_default(homescreen)
|
||||||
return
|
else:
|
||||||
|
await recovery_process()
|
||||||
await recovery_process()
|
|
||||||
|
|
||||||
|
|
||||||
async def recovery_process() -> Success:
|
async def recovery_process() -> Success:
|
||||||
import storage
|
import storage
|
||||||
from trezor.enums import MessageType
|
from trezor.enums import MessageType, RecoveryKind
|
||||||
|
|
||||||
wire.AVOID_RESTARTING_FOR = (MessageType.Initialize, MessageType.GetFeatures)
|
is_special_kind = storage_recovery.get_kind() in (
|
||||||
|
RecoveryKind.DryRun,
|
||||||
|
RecoveryKind.UnlockRepeatedBackup,
|
||||||
|
)
|
||||||
|
|
||||||
|
wire.AVOID_RESTARTING_FOR = (
|
||||||
|
MessageType.Initialize,
|
||||||
|
MessageType.GetFeatures,
|
||||||
|
MessageType.EndSession,
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
return await _continue_recovery_process()
|
return await _continue_recovery_process()
|
||||||
except recover.RecoveryAborted:
|
except recover.RecoveryAborted:
|
||||||
dry_run = storage_recovery.is_dry_run()
|
if is_special_kind:
|
||||||
if dry_run:
|
|
||||||
storage_recovery.end_progress()
|
storage_recovery.end_progress()
|
||||||
else:
|
else:
|
||||||
storage.wipe()
|
storage.wipe()
|
||||||
raise wire.ActionCancelled
|
raise wire.ActionCancelled
|
||||||
|
|
||||||
|
|
||||||
|
async def _continue_repeated_backup() -> None:
|
||||||
|
from trezor import workflow
|
||||||
|
from trezor.enums import ButtonRequestType, MessageType
|
||||||
|
from trezor.ui.layouts import confirm_action
|
||||||
|
from trezor.wire import ActionCancelled
|
||||||
|
|
||||||
|
from apps import workflow_handlers
|
||||||
|
from apps.common import mnemonic
|
||||||
|
from apps.homescreen import homescreen
|
||||||
|
from apps.management.reset_device import backup_seed
|
||||||
|
|
||||||
|
wire.AVOID_RESTARTING_FOR = (
|
||||||
|
MessageType.Initialize,
|
||||||
|
MessageType.GetFeatures,
|
||||||
|
MessageType.EndSession,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
await confirm_action(
|
||||||
|
"confirm_repeated_backup",
|
||||||
|
TR.recovery__title_unlock_repeated_backup,
|
||||||
|
description=TR.recovery__unlock_repeated_backup,
|
||||||
|
br_code=ButtonRequestType.ProtectCall,
|
||||||
|
verb=TR.recovery__unlock_repeated_backup_verb,
|
||||||
|
)
|
||||||
|
|
||||||
|
mnemonic_secret, backup_type = mnemonic.get()
|
||||||
|
if mnemonic_secret is None:
|
||||||
|
raise RuntimeError
|
||||||
|
|
||||||
|
await backup_seed(backup_type, mnemonic_secret)
|
||||||
|
except ActionCancelled:
|
||||||
|
workflow.set_default(homescreen)
|
||||||
|
finally:
|
||||||
|
storage_cache.delete(storage_cache.APP_RECOVERY_REPEATED_BACKUP_UNLOCKED)
|
||||||
|
wire.find_handler = workflow_handlers.find_registered_handler
|
||||||
|
storage_recovery.end_progress()
|
||||||
|
|
||||||
|
|
||||||
async def _continue_recovery_process() -> Success:
|
async def _continue_recovery_process() -> Success:
|
||||||
from trezor import utils
|
from trezor import utils
|
||||||
|
from trezor.enums import RecoveryKind
|
||||||
from trezor.errors import MnemonicError
|
from trezor.errors import MnemonicError
|
||||||
|
|
||||||
# gather the current recovery state from storage
|
# gather the current recovery state from storage
|
||||||
dry_run = storage_recovery.is_dry_run()
|
kind = storage_recovery.get_kind()
|
||||||
word_count, backup_type = recover.load_slip39_state()
|
word_count, backup_type = recover.load_slip39_state()
|
||||||
|
|
||||||
# Both word_count and backup_type are derived from the same data. Both will be
|
# Both word_count and backup_type are derived from the same data. Both will be
|
||||||
@ -59,7 +109,7 @@ async def _continue_recovery_process() -> Success:
|
|||||||
if not is_first_step:
|
if not is_first_step:
|
||||||
assert word_count is not None
|
assert word_count is not None
|
||||||
# If we continue recovery, show starting screen with word count immediately.
|
# If we continue recovery, show starting screen with word count immediately.
|
||||||
await _request_share_first_screen(word_count)
|
await _request_share_first_screen(word_count, kind)
|
||||||
|
|
||||||
secret = None
|
secret = None
|
||||||
while secret is None:
|
while secret is None:
|
||||||
@ -72,9 +122,9 @@ async def _continue_recovery_process() -> Success:
|
|||||||
TR.buttons__continue, TR.recovery__num_of_words
|
TR.buttons__continue, TR.recovery__num_of_words
|
||||||
)
|
)
|
||||||
# ask for the number of words
|
# ask for the number of words
|
||||||
word_count = await layout.request_word_count(dry_run)
|
word_count = await layout.request_word_count(kind == RecoveryKind.DryRun)
|
||||||
# ...and only then show the starting screen with word count.
|
# ...and only then show the starting screen with word count.
|
||||||
await _request_share_first_screen(word_count)
|
await _request_share_first_screen(word_count, kind)
|
||||||
assert word_count is not None
|
assert word_count is not None
|
||||||
|
|
||||||
# ask for mnemonic words one by one
|
# ask for mnemonic words one by one
|
||||||
@ -94,23 +144,22 @@ async def _continue_recovery_process() -> Success:
|
|||||||
await layout.show_invalid_mnemonic(word_count)
|
await layout.show_invalid_mnemonic(word_count)
|
||||||
|
|
||||||
assert backup_type is not None
|
assert backup_type is not None
|
||||||
if dry_run:
|
if kind == RecoveryKind.DryRun:
|
||||||
result = await _finish_recovery_dry_run(secret, backup_type)
|
result = await _finish_recovery_dry_run(secret, backup_type)
|
||||||
|
elif kind == RecoveryKind.UnlockRepeatedBackup:
|
||||||
|
result = await _finish_recovery_unlock_repeated_backup(secret, backup_type)
|
||||||
else:
|
else:
|
||||||
result = await _finish_recovery(secret, backup_type)
|
result = await _finish_recovery(secret, backup_type)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
async def _finish_recovery_dry_run(secret: bytes, backup_type: BackupType) -> Success:
|
def _check_secret_against_stored_secret(secret: bytes, is_slip39: bool, backup_type: BackupType) -> bool:
|
||||||
from trezor import utils
|
from trezor import utils
|
||||||
from trezor.crypto.hashlib import sha256
|
from trezor.crypto.hashlib import sha256
|
||||||
|
|
||||||
from apps.common import mnemonic
|
from apps.common import mnemonic
|
||||||
|
|
||||||
if backup_type is None:
|
|
||||||
raise RuntimeError
|
|
||||||
|
|
||||||
digest_input = sha256(secret).digest()
|
digest_input = sha256(secret).digest()
|
||||||
stored = mnemonic.get_secret()
|
stored = mnemonic.get_secret()
|
||||||
digest_stored = sha256(stored).digest()
|
digest_stored = sha256(stored).digest()
|
||||||
@ -132,6 +181,17 @@ async def _finish_recovery_dry_run(secret: bytes, backup_type: BackupType) -> Su
|
|||||||
== storage_recovery.get_slip39_iteration_exponent()
|
== storage_recovery.get_slip39_iteration_exponent()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
async def _finish_recovery_dry_run(secret: bytes, backup_type: BackupType) -> Success:
|
||||||
|
if backup_type is None:
|
||||||
|
raise RuntimeError
|
||||||
|
|
||||||
|
is_slip39 = backup_types.is_slip39_backup_type(backup_type)
|
||||||
|
|
||||||
|
result = _check_secret_against_stored_secret(secret, is_slip39, backup_type)
|
||||||
|
|
||||||
storage_recovery.end_progress()
|
storage_recovery.end_progress()
|
||||||
|
|
||||||
await layout.show_dry_run_result(result, is_slip39)
|
await layout.show_dry_run_result(result, is_slip39)
|
||||||
@ -142,6 +202,27 @@ async def _finish_recovery_dry_run(secret: bytes, backup_type: BackupType) -> Su
|
|||||||
raise wire.ProcessError("The seed does not match the one in the device")
|
raise wire.ProcessError("The seed does not match the one in the device")
|
||||||
|
|
||||||
|
|
||||||
|
async def _finish_recovery_unlock_repeated_backup(
|
||||||
|
secret: bytes, backup_type: BackupType
|
||||||
|
) -> Success:
|
||||||
|
import storage.cache as storage_cache
|
||||||
|
|
||||||
|
if backup_type is None:
|
||||||
|
raise RuntimeError
|
||||||
|
|
||||||
|
is_slip39 = backup_types.is_slip39_backup_type(backup_type)
|
||||||
|
|
||||||
|
result = _check_secret_against_stored_secret(secret, is_slip39, backup_type)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
storage_cache.set_bool(
|
||||||
|
storage_cache.APP_RECOVERY_REPEATED_BACKUP_UNLOCKED, True
|
||||||
|
)
|
||||||
|
return Success(message="Backup unlocked")
|
||||||
|
else:
|
||||||
|
raise wire.ProcessError("The seed does not match the one in the device")
|
||||||
|
|
||||||
|
|
||||||
async def _finish_recovery(secret: bytes, backup_type: BackupType) -> Success:
|
async def _finish_recovery(secret: bytes, backup_type: BackupType) -> Success:
|
||||||
from trezor.ui.layouts import show_success
|
from trezor.ui.layouts import show_success
|
||||||
|
|
||||||
@ -191,15 +272,23 @@ async def _process_words(words: str) -> tuple[bytes | None, BackupType]:
|
|||||||
return secret, backup_type
|
return secret, backup_type
|
||||||
|
|
||||||
|
|
||||||
async def _request_share_first_screen(word_count: int) -> None:
|
async def _request_share_first_screen(word_count: int, kind: RecoveryKind) -> None:
|
||||||
|
from trezor.enums import RecoveryKind
|
||||||
|
|
||||||
if backup_types.is_slip39_word_count(word_count):
|
if backup_types.is_slip39_word_count(word_count):
|
||||||
remaining = storage_recovery.fetch_slip39_remaining_shares()
|
remaining = storage_recovery.fetch_slip39_remaining_shares()
|
||||||
if remaining:
|
if remaining:
|
||||||
await _request_share_next_screen()
|
await _request_share_next_screen()
|
||||||
else:
|
else:
|
||||||
|
if kind == RecoveryKind.UnlockRepeatedBackup:
|
||||||
|
text = TR.recovery__enter_backup
|
||||||
|
button_label = TR.buttons__continue
|
||||||
|
else:
|
||||||
|
text = TR.recovery__enter_any_share
|
||||||
|
button_label = TR.buttons__enter_share
|
||||||
await layout.homescreen_dialog(
|
await layout.homescreen_dialog(
|
||||||
TR.buttons__enter_share,
|
button_label,
|
||||||
TR.recovery__enter_any_share,
|
text,
|
||||||
TR.recovery__word_count_template.format(word_count),
|
TR.recovery__word_count_template.format(word_count),
|
||||||
show_info=True,
|
show_info=True,
|
||||||
)
|
)
|
||||||
|
@ -149,21 +149,23 @@ async def homescreen_dialog(
|
|||||||
show_info: bool = False,
|
show_info: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
import storage.recovery as storage_recovery
|
import storage.recovery as storage_recovery
|
||||||
|
from trezor.enums import RecoveryKind
|
||||||
from trezor.ui.layouts.recovery import continue_recovery
|
from trezor.ui.layouts.recovery import continue_recovery
|
||||||
from trezor.wire import ActionCancelled
|
from trezor.wire import ActionCancelled
|
||||||
|
|
||||||
from .recover import RecoveryAborted
|
from .recover import RecoveryAborted
|
||||||
|
|
||||||
|
kind = storage_recovery.get_kind()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
dry_run = storage_recovery.is_dry_run()
|
|
||||||
if await continue_recovery(
|
if await continue_recovery(
|
||||||
button_label, text, subtext, info_func, dry_run, show_info
|
button_label, text, subtext, info_func, kind, show_info
|
||||||
):
|
):
|
||||||
# go forward in the recovery process
|
# go forward in the recovery process
|
||||||
break
|
break
|
||||||
# user has chosen to abort, confirm the choice
|
# user has chosen to abort, confirm the choice
|
||||||
try:
|
try:
|
||||||
await _confirm_abort(dry_run)
|
await _confirm_abort(kind != RecoveryKind.NormalRecovery)
|
||||||
except ActionCancelled:
|
except ActionCancelled:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
@ -34,7 +34,7 @@ APP_COMMON_REQUEST_PIN_LAST_UNLOCK = const(3 | _SESSIONLESS_FLAG)
|
|||||||
APP_COMMON_BUSY_DEADLINE_MS = const(4 | _SESSIONLESS_FLAG)
|
APP_COMMON_BUSY_DEADLINE_MS = const(4 | _SESSIONLESS_FLAG)
|
||||||
APP_MISC_COSI_NONCE = const(5 | _SESSIONLESS_FLAG)
|
APP_MISC_COSI_NONCE = const(5 | _SESSIONLESS_FLAG)
|
||||||
APP_MISC_COSI_COMMITMENT = const(6 | _SESSIONLESS_FLAG)
|
APP_MISC_COSI_COMMITMENT = const(6 | _SESSIONLESS_FLAG)
|
||||||
|
APP_RECOVERY_REPEATED_BACKUP_UNLOCKED = const(7 | _SESSIONLESS_FLAG)
|
||||||
|
|
||||||
# === Homescreen storage ===
|
# === Homescreen storage ===
|
||||||
# This does not logically belong to the "cache" functionality, but the cache module is
|
# This does not logically belong to the "cache" functionality, but the cache module is
|
||||||
@ -57,7 +57,7 @@ class InvalidSessionError(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class DataCache:
|
class DataCache:
|
||||||
fields: Sequence[int]
|
fields: Sequence[int] # field sizes
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.data = [bytearray(f + 1) for f in self.fields]
|
self.data = [bytearray(f + 1) for f in self.fields]
|
||||||
@ -145,6 +145,7 @@ class SessionlessCache(DataCache):
|
|||||||
8, # APP_COMMON_BUSY_DEADLINE_MS
|
8, # APP_COMMON_BUSY_DEADLINE_MS
|
||||||
32, # APP_MISC_COSI_NONCE
|
32, # APP_MISC_COSI_NONCE
|
||||||
32, # APP_MISC_COSI_COMMITMENT
|
32, # APP_MISC_COSI_COMMITMENT
|
||||||
|
0, # APP_RECOVERY_REPEATED_BACKUP_UNLOCKED
|
||||||
)
|
)
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
@ -233,13 +234,17 @@ def set(key: int, value: bytes) -> None:
|
|||||||
_SESSIONS[_active_session_idx].set(key, value)
|
_SESSIONS[_active_session_idx].set(key, value)
|
||||||
|
|
||||||
|
|
||||||
def set_int(key: int, value: int) -> None:
|
def _get_length(key: int) -> int:
|
||||||
if key & _SESSIONLESS_FLAG:
|
if key & _SESSIONLESS_FLAG:
|
||||||
length = _SESSIONLESS_CACHE.fields[key ^ _SESSIONLESS_FLAG]
|
return _SESSIONLESS_CACHE.fields[key ^ _SESSIONLESS_FLAG]
|
||||||
elif _active_session_idx is None:
|
elif _active_session_idx is None:
|
||||||
raise InvalidSessionError
|
raise InvalidSessionError
|
||||||
else:
|
else:
|
||||||
length = _SESSIONS[_active_session_idx].fields[key]
|
return _SESSIONS[_active_session_idx].fields[key]
|
||||||
|
|
||||||
|
|
||||||
|
def set_int(key: int, value: int) -> None:
|
||||||
|
length = _get_length(key)
|
||||||
|
|
||||||
encoded = value.to_bytes(length, "big")
|
encoded = value.to_bytes(length, "big")
|
||||||
|
|
||||||
@ -250,6 +255,14 @@ def set_int(key: int, value: int) -> None:
|
|||||||
set(key, encoded)
|
set(key, encoded)
|
||||||
|
|
||||||
|
|
||||||
|
def set_bool(key: int, value: bool) -> None:
|
||||||
|
assert _get_length(key) == 0 # skipping get_length in production build
|
||||||
|
if value:
|
||||||
|
set(key, b"")
|
||||||
|
else:
|
||||||
|
delete(key)
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
@ -276,6 +289,10 @@ def get_int(key: int, default: T | None = None) -> int | T | None: # noqa: F811
|
|||||||
return int.from_bytes(encoded, "big")
|
return int.from_bytes(encoded, "big")
|
||||||
|
|
||||||
|
|
||||||
|
def get_bool(key: int) -> bool: # noqa: F811
|
||||||
|
return get(key) is not None
|
||||||
|
|
||||||
|
|
||||||
def get_int_all_sessions(key: int) -> builtins.set[int]:
|
def get_int_all_sessions(key: int) -> builtins.set[int]:
|
||||||
sessions = [_SESSIONLESS_CACHE] if key & _SESSIONLESS_FLAG else _SESSIONS
|
sessions = [_SESSIONLESS_CACHE] if key & _SESSIONLESS_FLAG else _SESSIONS
|
||||||
values = builtins.set()
|
values = builtins.set()
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
from micropython import const
|
from micropython import const
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from storage import common
|
from storage import common
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from trezor.enums import RecoveryKind
|
||||||
|
|
||||||
# Namespace:
|
# Namespace:
|
||||||
_NAMESPACE = common.APP_RECOVERY
|
_NAMESPACE = common.APP_RECOVERY
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
# Keys:
|
# Keys:
|
||||||
_IN_PROGRESS = const(0x00) # bool
|
_IN_PROGRESS = const(0x00) # bool
|
||||||
_DRY_RUN = const(0x01) # bool
|
_KIND = const(0x01) # int
|
||||||
_SLIP39_IDENTIFIER = const(0x03) # bytes
|
_SLIP39_IDENTIFIER = const(0x03) # bytes
|
||||||
_REMAINING = const(0x05) # int
|
_REMAINING = const(0x05) # int
|
||||||
_SLIP39_ITERATION_EXPONENT = const(0x06) # int
|
_SLIP39_ITERATION_EXPONENT = const(0x06) # int
|
||||||
@ -37,14 +41,27 @@ def is_in_progress() -> bool:
|
|||||||
return common.get_bool(_NAMESPACE, _IN_PROGRESS)
|
return common.get_bool(_NAMESPACE, _IN_PROGRESS)
|
||||||
|
|
||||||
|
|
||||||
def set_dry_run(val: bool) -> None:
|
def set_kind(val: int) -> None:
|
||||||
_require_progress()
|
_require_progress()
|
||||||
common.set_bool(_NAMESPACE, _DRY_RUN, val)
|
common.set_uint8(_NAMESPACE, _KIND, val)
|
||||||
|
|
||||||
|
|
||||||
def is_dry_run() -> bool:
|
def get_kind() -> RecoveryKind:
|
||||||
|
from trezor.enums import RecoveryKind
|
||||||
|
|
||||||
_require_progress()
|
_require_progress()
|
||||||
return common.get_bool(_NAMESPACE, _DRY_RUN)
|
recovery_kind = common.get_uint8(_NAMESPACE, _KIND)
|
||||||
|
if recovery_kind is None:
|
||||||
|
recovery_kind = RecoveryKind.NormalRecovery
|
||||||
|
|
||||||
|
if recovery_kind not in (
|
||||||
|
RecoveryKind.NormalRecovery,
|
||||||
|
RecoveryKind.DryRun,
|
||||||
|
RecoveryKind.UnlockRepeatedBackup,
|
||||||
|
):
|
||||||
|
# Invalid recovery kind
|
||||||
|
raise RuntimeError
|
||||||
|
return recovery_kind
|
||||||
|
|
||||||
|
|
||||||
def set_slip39_identifier(identifier: int) -> None:
|
def set_slip39_identifier(identifier: int) -> None:
|
||||||
@ -129,7 +146,7 @@ def end_progress() -> None:
|
|||||||
_require_progress()
|
_require_progress()
|
||||||
for key in (
|
for key in (
|
||||||
_IN_PROGRESS,
|
_IN_PROGRESS,
|
||||||
_DRY_RUN,
|
_KIND,
|
||||||
_SLIP39_IDENTIFIER,
|
_SLIP39_IDENTIFIER,
|
||||||
_REMAINING,
|
_REMAINING,
|
||||||
_SLIP39_ITERATION_EXPONENT,
|
_SLIP39_ITERATION_EXPONENT,
|
||||||
|
7
core/src/trezor/enums/RecoveryKind.py
generated
Normal file
7
core/src/trezor/enums/RecoveryKind.py
generated
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Automatically generated by pb2py
|
||||||
|
# fmt: off
|
||||||
|
# isort:skip_file
|
||||||
|
|
||||||
|
NormalRecovery = 0
|
||||||
|
DryRun = 1
|
||||||
|
UnlockRepeatedBackup = 2
|
8
core/src/trezor/enums/RecoveryStatus.py
generated
Normal file
8
core/src/trezor/enums/RecoveryStatus.py
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Automatically generated by pb2py
|
||||||
|
# fmt: off
|
||||||
|
# isort:skip_file
|
||||||
|
|
||||||
|
NoRecovery = 0
|
||||||
|
InNormalRecovery = 1
|
||||||
|
InDryRunRecovery = 2
|
||||||
|
InUnlockRepeatedBackupRecovery = 3
|
11
core/src/trezor/enums/__init__.py
generated
11
core/src/trezor/enums/__init__.py
generated
@ -440,6 +440,12 @@ if TYPE_CHECKING:
|
|||||||
Jpeg = 2
|
Jpeg = 2
|
||||||
ToiG = 3
|
ToiG = 3
|
||||||
|
|
||||||
|
class RecoveryStatus(IntEnum):
|
||||||
|
NoRecovery = 0
|
||||||
|
InNormalRecovery = 1
|
||||||
|
InDryRunRecovery = 2
|
||||||
|
InUnlockRepeatedBackupRecovery = 3
|
||||||
|
|
||||||
class Capability(IntEnum):
|
class Capability(IntEnum):
|
||||||
Bitcoin = 1
|
Bitcoin = 1
|
||||||
Bitcoin_like = 2
|
Bitcoin_like = 2
|
||||||
@ -472,6 +478,11 @@ if TYPE_CHECKING:
|
|||||||
ScrambledWords = 0
|
ScrambledWords = 0
|
||||||
Matrix = 1
|
Matrix = 1
|
||||||
|
|
||||||
|
class RecoveryKind(IntEnum):
|
||||||
|
NormalRecovery = 0
|
||||||
|
DryRun = 1
|
||||||
|
UnlockRepeatedBackup = 2
|
||||||
|
|
||||||
class WordRequestType(IntEnum):
|
class WordRequestType(IntEnum):
|
||||||
Plain = 0
|
Plain = 0
|
||||||
Matrix9 = 1
|
Matrix9 = 1
|
||||||
|
10
core/src/trezor/messages.py
generated
10
core/src/trezor/messages.py
generated
@ -54,6 +54,8 @@ if TYPE_CHECKING:
|
|||||||
from trezor.enums import OutputScriptType # noqa: F401
|
from trezor.enums import OutputScriptType # noqa: F401
|
||||||
from trezor.enums import PinMatrixRequestType # noqa: F401
|
from trezor.enums import PinMatrixRequestType # noqa: F401
|
||||||
from trezor.enums import RecoveryDeviceType # noqa: F401
|
from trezor.enums import RecoveryDeviceType # noqa: F401
|
||||||
|
from trezor.enums import RecoveryKind # noqa: F401
|
||||||
|
from trezor.enums import RecoveryStatus # noqa: F401
|
||||||
from trezor.enums import RequestType # noqa: F401
|
from trezor.enums import RequestType # noqa: F401
|
||||||
from trezor.enums import SafetyCheckLevel # noqa: F401
|
from trezor.enums import SafetyCheckLevel # noqa: F401
|
||||||
from trezor.enums import SdProtectOperationType # noqa: F401
|
from trezor.enums import SdProtectOperationType # noqa: F401
|
||||||
@ -2140,7 +2142,7 @@ if TYPE_CHECKING:
|
|||||||
fw_vendor: "str | None"
|
fw_vendor: "str | None"
|
||||||
unfinished_backup: "bool | None"
|
unfinished_backup: "bool | None"
|
||||||
no_backup: "bool | None"
|
no_backup: "bool | None"
|
||||||
recovery_mode: "bool | None"
|
recovery_status: "RecoveryStatus | None"
|
||||||
capabilities: "list[Capability]"
|
capabilities: "list[Capability]"
|
||||||
backup_type: "BackupType | None"
|
backup_type: "BackupType | None"
|
||||||
sd_card_present: "bool | None"
|
sd_card_present: "bool | None"
|
||||||
@ -2194,7 +2196,7 @@ if TYPE_CHECKING:
|
|||||||
fw_vendor: "str | None" = None,
|
fw_vendor: "str | None" = None,
|
||||||
unfinished_backup: "bool | None" = None,
|
unfinished_backup: "bool | None" = None,
|
||||||
no_backup: "bool | None" = None,
|
no_backup: "bool | None" = None,
|
||||||
recovery_mode: "bool | None" = None,
|
recovery_status: "RecoveryStatus | None" = None,
|
||||||
backup_type: "BackupType | None" = None,
|
backup_type: "BackupType | None" = None,
|
||||||
sd_card_present: "bool | None" = None,
|
sd_card_present: "bool | None" = None,
|
||||||
sd_protection: "bool | None" = None,
|
sd_protection: "bool | None" = None,
|
||||||
@ -2600,7 +2602,7 @@ if TYPE_CHECKING:
|
|||||||
enforce_wordlist: "bool | None"
|
enforce_wordlist: "bool | None"
|
||||||
type: "RecoveryDeviceType | None"
|
type: "RecoveryDeviceType | None"
|
||||||
u2f_counter: "int | None"
|
u2f_counter: "int | None"
|
||||||
dry_run: "bool | None"
|
kind: "RecoveryKind"
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -2612,7 +2614,7 @@ if TYPE_CHECKING:
|
|||||||
enforce_wordlist: "bool | None" = None,
|
enforce_wordlist: "bool | None" = None,
|
||||||
type: "RecoveryDeviceType | None" = None,
|
type: "RecoveryDeviceType | None" = None,
|
||||||
u2f_counter: "int | None" = None,
|
u2f_counter: "int | None" = None,
|
||||||
dry_run: "bool | None" = None,
|
kind: "RecoveryKind | None" = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ from typing import Callable, Iterable
|
|||||||
|
|
||||||
import trezorui2
|
import trezorui2
|
||||||
from trezor import TR
|
from trezor import TR
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType, RecoveryKind
|
||||||
|
|
||||||
from ..common import interact
|
from ..common import interact
|
||||||
from . import RustLayout, raise_if_not_confirmed, show_warning
|
from . import RustLayout, raise_if_not_confirmed, show_warning
|
||||||
@ -76,7 +76,7 @@ async def continue_recovery(
|
|||||||
text: str,
|
text: str,
|
||||||
subtext: str | None,
|
subtext: str | None,
|
||||||
info_func: Callable | None,
|
info_func: Callable | None,
|
||||||
dry_run: bool,
|
kind: RecoveryKind,
|
||||||
show_info: bool = False,
|
show_info: bool = False,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
# TODO: implement info_func?
|
# TODO: implement info_func?
|
||||||
@ -84,7 +84,7 @@ async def continue_recovery(
|
|||||||
# (and having middle button would mean shortening the right button text)
|
# (and having middle button would mean shortening the right button text)
|
||||||
|
|
||||||
# Never showing info for dry-run, user already saw it and it is disturbing
|
# Never showing info for dry-run, user already saw it and it is disturbing
|
||||||
if dry_run:
|
if kind in (RecoveryKind.DryRun, RecoveryKind.UnlockRepeatedBackup):
|
||||||
show_info = False
|
show_info = False
|
||||||
|
|
||||||
if subtext:
|
if subtext:
|
||||||
@ -95,8 +95,8 @@ async def continue_recovery(
|
|||||||
title="",
|
title="",
|
||||||
description=text,
|
description=text,
|
||||||
button=button_label,
|
button=button_label,
|
||||||
|
kind=kind,
|
||||||
info_button=False,
|
info_button=False,
|
||||||
dry_run=dry_run,
|
|
||||||
show_info=show_info, # type: ignore [No parameter named "show_info"]
|
show_info=show_info, # type: ignore [No parameter named "show_info"]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -2,7 +2,7 @@ from typing import Callable, Iterable
|
|||||||
|
|
||||||
import trezorui2
|
import trezorui2
|
||||||
from trezor import TR
|
from trezor import TR
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType, RecoveryKind
|
||||||
|
|
||||||
from ..common import interact
|
from ..common import interact
|
||||||
from . import RustLayout, raise_if_not_confirmed
|
from . import RustLayout, raise_if_not_confirmed
|
||||||
@ -116,7 +116,7 @@ async def continue_recovery(
|
|||||||
text: str,
|
text: str,
|
||||||
subtext: str | None,
|
subtext: str | None,
|
||||||
info_func: Callable | None,
|
info_func: Callable | None,
|
||||||
dry_run: bool,
|
kind: RecoveryKind,
|
||||||
show_info: bool = False, # unused on TT
|
show_info: bool = False, # unused on TT
|
||||||
) -> bool:
|
) -> bool:
|
||||||
from ..common import button_request
|
from ..common import button_request
|
||||||
@ -132,8 +132,8 @@ async def continue_recovery(
|
|||||||
title=text,
|
title=text,
|
||||||
description=description,
|
description=description,
|
||||||
button=button_label,
|
button=button_label,
|
||||||
|
kind=kind,
|
||||||
info_button=info_func is not None,
|
info_button=info_func is not None,
|
||||||
dry_run=dry_run,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -516,7 +516,6 @@
|
|||||||
"recovery__num_of_words": "text,2",
|
"recovery__num_of_words": "text,2",
|
||||||
"recovery__only_first_n_letters": "text,4",
|
"recovery__only_first_n_letters": "text,4",
|
||||||
"recovery__progress_will_be_lost": "text,2",
|
"recovery__progress_will_be_lost": "text,2",
|
||||||
"recovery__select_num_of_words": "text,3",
|
|
||||||
"recovery__share_already_entered": "text,2",
|
"recovery__share_already_entered": "text,2",
|
||||||
"recovery__share_from_another_multi_share_backup": "text,3",
|
"recovery__share_from_another_multi_share_backup": "text,3",
|
||||||
"recovery__share_num_template": "text,1",
|
"recovery__share_num_template": "text,1",
|
||||||
|
@ -562,7 +562,6 @@
|
|||||||
"recovery__num_of_words": "Vyberte počet slov v záloze.",
|
"recovery__num_of_words": "Vyberte počet slov v záloze.",
|
||||||
"recovery__only_first_n_letters": "Musíte vybrat pouze první 2-4 písmena každého slova.",
|
"recovery__only_first_n_letters": "Musíte vybrat pouze první 2-4 písmena každého slova.",
|
||||||
"recovery__progress_will_be_lost": "Veškerý postup bude ztracen.",
|
"recovery__progress_will_be_lost": "Veškerý postup bude ztracen.",
|
||||||
"recovery__select_num_of_words": "Vyberte počet slov v záloze.",
|
|
||||||
"recovery__share_already_entered": "Část jste již zadali",
|
"recovery__share_already_entered": "Část jste již zadali",
|
||||||
"recovery__share_from_another_multi_share_backup": "Zadali jste část z jiné zálohy Shamir.",
|
"recovery__share_from_another_multi_share_backup": "Zadali jste část z jiné zálohy Shamir.",
|
||||||
"recovery__share_num_template": "Část {0}",
|
"recovery__share_num_template": "Část {0}",
|
||||||
|
@ -562,7 +562,6 @@
|
|||||||
"recovery__num_of_words": "Wörteranzahl in deinem Backup auswählen.",
|
"recovery__num_of_words": "Wörteranzahl in deinem Backup auswählen.",
|
||||||
"recovery__only_first_n_letters": "Du musst nur die ersten 2-4 Buchstaben jedes Worts auswählen.",
|
"recovery__only_first_n_letters": "Du musst nur die ersten 2-4 Buchstaben jedes Worts auswählen.",
|
||||||
"recovery__progress_will_be_lost": "Alle Fortschritte gehen verloren.",
|
"recovery__progress_will_be_lost": "Alle Fortschritte gehen verloren.",
|
||||||
"recovery__select_num_of_words": "Wörteranzahl in deinem Backup auswählen.",
|
|
||||||
"recovery__share_already_entered": "Share wurde bereits eingegeben",
|
"recovery__share_already_entered": "Share wurde bereits eingegeben",
|
||||||
"recovery__share_from_another_multi_share_backup": "Share eines anderen Shamir-Backups eingegeben.",
|
"recovery__share_from_another_multi_share_backup": "Share eines anderen Shamir-Backups eingegeben.",
|
||||||
"recovery__share_num_template": "Share {0}",
|
"recovery__share_num_template": "Share {0}",
|
||||||
|
@ -577,7 +577,6 @@
|
|||||||
"recovery__num_of_words": "Select the number of words in your backup.",
|
"recovery__num_of_words": "Select the number of words in your backup.",
|
||||||
"recovery__only_first_n_letters": "You'll only have to select the first 2-4 letters of each word.",
|
"recovery__only_first_n_letters": "You'll only have to select the first 2-4 letters of each word.",
|
||||||
"recovery__progress_will_be_lost": "All progress will be lost.",
|
"recovery__progress_will_be_lost": "All progress will be lost.",
|
||||||
"recovery__select_num_of_words": "Select the number of words in your backup.",
|
|
||||||
"recovery__share_already_entered": "Share already entered",
|
"recovery__share_already_entered": "Share already entered",
|
||||||
"recovery__share_does_not_match": "Share doesn't match",
|
"recovery__share_does_not_match": "Share doesn't match",
|
||||||
"recovery__share_from_another_multi_share_backup": "You have entered a share from a different backup.",
|
"recovery__share_from_another_multi_share_backup": "You have entered a share from a different backup.",
|
||||||
@ -588,7 +587,10 @@
|
|||||||
"recovery__title_dry_run": "Backup check",
|
"recovery__title_dry_run": "Backup check",
|
||||||
"recovery__title_recover": "Recover wallet",
|
"recovery__title_recover": "Recover wallet",
|
||||||
"recovery__title_remaining_shares": "Remaining shares",
|
"recovery__title_remaining_shares": "Remaining shares",
|
||||||
|
"recovery__title_unlock_repeated_backup": "Multi-share backup",
|
||||||
"recovery__type_word_x_of_y_template": "Type word {0} of {1}",
|
"recovery__type_word_x_of_y_template": "Type word {0} of {1}",
|
||||||
|
"recovery__unlock_repeated_backup": "Do you want to unlock the seed for repeated backup?",
|
||||||
|
"recovery__unlock_repeated_backup_verb": "Unlock backup",
|
||||||
"recovery__wallet_recovered": "Wallet recovery completed",
|
"recovery__wallet_recovered": "Wallet recovery completed",
|
||||||
"recovery__wanna_cancel_dry_run": "Are you sure you want to cancel the backup check?",
|
"recovery__wanna_cancel_dry_run": "Are you sure you want to cancel the backup check?",
|
||||||
"recovery__wanna_cancel_recovery": "Are you sure you want to cancel the recovery process?",
|
"recovery__wanna_cancel_recovery": "Are you sure you want to cancel the recovery process?",
|
||||||
|
@ -562,7 +562,6 @@
|
|||||||
"recovery__num_of_words": "Elige el nro.de p. de la copia seg.",
|
"recovery__num_of_words": "Elige el nro.de p. de la copia seg.",
|
||||||
"recovery__only_first_n_letters": "Solo tendrás que seleccionar las primeras 2-4 letras de cada palabra.",
|
"recovery__only_first_n_letters": "Solo tendrás que seleccionar las primeras 2-4 letras de cada palabra.",
|
||||||
"recovery__progress_will_be_lost": "Se perderá todo el progreso.",
|
"recovery__progress_will_be_lost": "Se perderá todo el progreso.",
|
||||||
"recovery__select_num_of_words": "Elige el nro.de p. de la copia seg.",
|
|
||||||
"recovery__share_already_entered": "Ya se ha introducido el recurso compartido",
|
"recovery__share_already_entered": "Ya se ha introducido el recurso compartido",
|
||||||
"recovery__share_from_another_multi_share_backup": "El rec. comp. es de otra copia de seguridad de Shamir.",
|
"recovery__share_from_another_multi_share_backup": "El rec. comp. es de otra copia de seguridad de Shamir.",
|
||||||
"recovery__share_num_template": "Recurso compartido {0}",
|
"recovery__share_num_template": "Recurso compartido {0}",
|
||||||
|
@ -562,7 +562,6 @@
|
|||||||
"recovery__num_of_words": "Sélect. le nbre de mots dans votre sauv.",
|
"recovery__num_of_words": "Sélect. le nbre de mots dans votre sauv.",
|
||||||
"recovery__only_first_n_letters": "Il vous suffit de sélectionner les 2 ou 4 premières lettres de chaque mot.",
|
"recovery__only_first_n_letters": "Il vous suffit de sélectionner les 2 ou 4 premières lettres de chaque mot.",
|
||||||
"recovery__progress_will_be_lost": "Toute progression sera perdue.",
|
"recovery__progress_will_be_lost": "Toute progression sera perdue.",
|
||||||
"recovery__select_num_of_words": "Sélect. le nbre de mots dans votre sauv.",
|
|
||||||
"recovery__share_already_entered": "Fragment déjà saisi",
|
"recovery__share_already_entered": "Fragment déjà saisi",
|
||||||
"recovery__share_from_another_multi_share_backup": "Vous avez saisi un fragm. d'une autre sauv. Shamir.",
|
"recovery__share_from_another_multi_share_backup": "Vous avez saisi un fragm. d'une autre sauv. Shamir.",
|
||||||
"recovery__share_num_template": "Fragment {0}",
|
"recovery__share_num_template": "Fragment {0}",
|
||||||
|
@ -931,5 +931,8 @@
|
|||||||
"929": "words__settings",
|
"929": "words__settings",
|
||||||
"930": "words__try_again",
|
"930": "words__try_again",
|
||||||
"931": "reset__slip39_checklist_num_groups_x_template",
|
"931": "reset__slip39_checklist_num_groups_x_template",
|
||||||
"932": "brightness__title"
|
"932": "brightness__title",
|
||||||
|
"933": "recovery__title_unlock_repeated_backup",
|
||||||
|
"934": "recovery__unlock_repeated_backup",
|
||||||
|
"935": "recovery__unlock_repeated_backup_verb"
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"current": {
|
"current": {
|
||||||
"merkle_root": "1e9cb4df51f267f250b45ba4c130f2e46487dc60d8f5e0e19bd398ec4a613f24",
|
"merkle_root": "45979e4f395fcad03d843869023d35047483512ec797063806998dc553823c72",
|
||||||
"datetime": "2024-05-28T09:19:32.720044",
|
"datetime": "2024-06-03T12:43:57.548072",
|
||||||
"commit": "ebb35e8de3ea870b13d85891096136c16bac2cc5"
|
"commit": "510121bad342d1f900ca123e131448d577a25e39"
|
||||||
},
|
},
|
||||||
"history": [
|
"history": [
|
||||||
{
|
{
|
||||||
|
@ -279,14 +279,24 @@ entering PIN) or standard recovery (with entering the seed to the host
|
|||||||
computer one by one in random order). The process continues with
|
computer one by one in random order). The process continues with
|
||||||
optional check of the seed validity and optional setting up the PIN,
|
optional check of the seed validity and optional setting up the PIN,
|
||||||
which has to be confirmed. Finally the recovered wallet is saved into
|
which has to be confirmed. Finally the recovered wallet is saved into
|
||||||
device storage. The same process is used with the dry run recovery, the
|
device storage.
|
||||||
|
|
||||||
|
The same process is used with the dry run recovery, the
|
||||||
differences are that this process can be done only with already
|
differences are that this process can be done only with already
|
||||||
initialized deviice and that the mnemonic is not saved into the device
|
initialized device and that the mnemonic is not saved into the device
|
||||||
but it is only compared to the mnemonic already loaded into the device
|
but it is only compared to the mnemonic already loaded into the device
|
||||||
with the successful result (The seed is valid and matches the one in the
|
with the successful result (The seed is valid and matches the one in the
|
||||||
device) or unsuccessful result(The seed is valid but does not match the
|
device) or unsuccessful result (The seed is valid but does not match the
|
||||||
one in the device).
|
one in the device).
|
||||||
|
|
||||||
|
A third kind of recovery is one that is done in order to
|
||||||
|
unlock a repeated backup. This is similar to the dry run recovery in that
|
||||||
|
the device needs to be already initialized and that the mnemonic entered
|
||||||
|
is compared against the one stored in the device. Once successful,
|
||||||
|
a special mode is activated, which allows an additional backup
|
||||||
|
to be performed. This is useful for upgrading SLIP39 backups
|
||||||
|
to multiple shares.
|
||||||
|
|
||||||
## LoadDevice
|
## LoadDevice
|
||||||
|
|
||||||
Load device lets user to load the device with the specific recovery
|
Load device lets user to load the device with the specific recovery
|
||||||
|
@ -491,7 +491,11 @@ void fsm_msgApplyFlags(const ApplyFlags *msg) {
|
|||||||
void fsm_msgRecoveryDevice(const RecoveryDevice *msg) {
|
void fsm_msgRecoveryDevice(const RecoveryDevice *msg) {
|
||||||
CHECK_PIN_UNCACHED
|
CHECK_PIN_UNCACHED
|
||||||
|
|
||||||
const bool dry_run = msg->has_dry_run ? msg->dry_run : false;
|
CHECK_PARAM(!msg->has_kind || msg->kind == RecoveryKind_NormalRecovery ||
|
||||||
|
msg->kind == RecoveryKind_DryRun,
|
||||||
|
_("UnlockRepeatedBackup not supported"))
|
||||||
|
|
||||||
|
const bool dry_run = msg->has_kind ? msg->kind == RecoveryKind_DryRun : false;
|
||||||
if (!dry_run) {
|
if (!dry_run) {
|
||||||
CHECK_NOT_INITIALIZED
|
CHECK_NOT_INITIALIZED
|
||||||
} else {
|
} else {
|
||||||
|
@ -151,6 +151,7 @@ def load(
|
|||||||
"-t", "--type", "rec_type", type=ChoiceType(RECOVERY_TYPE), default="scrambled"
|
"-t", "--type", "rec_type", type=ChoiceType(RECOVERY_TYPE), default="scrambled"
|
||||||
)
|
)
|
||||||
@click.option("-d", "--dry-run", is_flag=True)
|
@click.option("-d", "--dry-run", is_flag=True)
|
||||||
|
@click.option("-b", "--unlock-repeated-backup", is_flag=True)
|
||||||
@with_client
|
@with_client
|
||||||
def recover(
|
def recover(
|
||||||
client: "TrezorClient",
|
client: "TrezorClient",
|
||||||
@ -162,6 +163,7 @@ def recover(
|
|||||||
u2f_counter: int,
|
u2f_counter: int,
|
||||||
rec_type: messages.RecoveryDeviceType,
|
rec_type: messages.RecoveryDeviceType,
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
|
unlock_repeated_backup: bool,
|
||||||
) -> "MessageType":
|
) -> "MessageType":
|
||||||
"""Start safe recovery workflow."""
|
"""Start safe recovery workflow."""
|
||||||
if rec_type == messages.RecoveryDeviceType.ScrambledWords:
|
if rec_type == messages.RecoveryDeviceType.ScrambledWords:
|
||||||
@ -170,6 +172,15 @@ def recover(
|
|||||||
input_callback = ui.matrix_words
|
input_callback = ui.matrix_words
|
||||||
click.echo(ui.RECOVERY_MATRIX_DESCRIPTION)
|
click.echo(ui.RECOVERY_MATRIX_DESCRIPTION)
|
||||||
|
|
||||||
|
if dry_run and unlock_repeated_backup:
|
||||||
|
raise click.ClickException("Cannot use -d and -b together.")
|
||||||
|
|
||||||
|
recovery_kind = None
|
||||||
|
if dry_run:
|
||||||
|
recovery_kind = messages.RecoveryKind.DryRun
|
||||||
|
if unlock_repeated_backup:
|
||||||
|
recovery_kind = messages.RecoveryKind.UnlockRepeatedBackup
|
||||||
|
|
||||||
return device.recover(
|
return device.recover(
|
||||||
client,
|
client,
|
||||||
word_count=int(words),
|
word_count=int(words),
|
||||||
@ -179,7 +190,7 @@ def recover(
|
|||||||
u2f_counter=u2f_counter,
|
u2f_counter=u2f_counter,
|
||||||
input_callback=input_callback,
|
input_callback=input_callback,
|
||||||
type=rec_type,
|
type=rec_type,
|
||||||
dry_run=dry_run,
|
recovery_kind=recovery_kind,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -160,8 +160,10 @@ def recover(
|
|||||||
language: Optional[str] = None,
|
language: Optional[str] = None,
|
||||||
input_callback: Optional[Callable] = None,
|
input_callback: Optional[Callable] = None,
|
||||||
type: messages.RecoveryDeviceType = messages.RecoveryDeviceType.ScrambledWords,
|
type: messages.RecoveryDeviceType = messages.RecoveryDeviceType.ScrambledWords,
|
||||||
dry_run: bool = False,
|
dry_run: Optional[bool] = None,
|
||||||
u2f_counter: Optional[int] = None,
|
u2f_counter: Optional[int] = None,
|
||||||
|
*,
|
||||||
|
recovery_kind: Optional[messages.RecoveryKind] = None,
|
||||||
) -> "MessageType":
|
) -> "MessageType":
|
||||||
if language is not None:
|
if language is not None:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
@ -169,13 +171,34 @@ def recover(
|
|||||||
DeprecationWarning,
|
DeprecationWarning,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if dry_run is not None:
|
||||||
|
warnings.warn(
|
||||||
|
"Use recovery_kind=RecoveryKind.DryRun instead!",
|
||||||
|
DeprecationWarning,
|
||||||
|
)
|
||||||
|
|
||||||
|
if recovery_kind is not None:
|
||||||
|
raise ValueError(
|
||||||
|
"Cannot use both dry_run and recovery_kind simultaneously."
|
||||||
|
)
|
||||||
|
elif dry_run:
|
||||||
|
recovery_kind = messages.RecoveryKind.DryRun
|
||||||
|
else:
|
||||||
|
recovery_kind = messages.RecoveryKind.NormalRecovery
|
||||||
|
|
||||||
|
if recovery_kind is None:
|
||||||
|
recovery_kind = messages.RecoveryKind.NormalRecovery
|
||||||
|
|
||||||
if client.features.model == "1" and input_callback is None:
|
if client.features.model == "1" and input_callback is None:
|
||||||
raise RuntimeError("Input callback required for Trezor One")
|
raise RuntimeError("Input callback required for Trezor One")
|
||||||
|
|
||||||
if word_count not in (12, 18, 24):
|
if word_count not in (12, 18, 24):
|
||||||
raise ValueError("Invalid word count. Use 12/18/24")
|
raise ValueError("Invalid word count. Use 12/18/24")
|
||||||
|
|
||||||
if client.features.initialized and not dry_run:
|
if (
|
||||||
|
client.features.initialized
|
||||||
|
and recovery_kind == messages.RecoveryKind.NormalRecovery
|
||||||
|
):
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Device already initialized. Call device.wipe() and try again."
|
"Device already initialized. Call device.wipe() and try again."
|
||||||
)
|
)
|
||||||
@ -184,10 +207,10 @@ def recover(
|
|||||||
u2f_counter = int(time.time())
|
u2f_counter = int(time.time())
|
||||||
|
|
||||||
msg = messages.RecoveryDevice(
|
msg = messages.RecoveryDevice(
|
||||||
word_count=word_count, enforce_wordlist=True, type=type, dry_run=dry_run
|
word_count=word_count, enforce_wordlist=True, type=type, kind=recovery_kind
|
||||||
)
|
)
|
||||||
|
|
||||||
if not dry_run:
|
if recovery_kind == messages.RecoveryKind.NormalRecovery:
|
||||||
# set additional parameters
|
# set additional parameters
|
||||||
msg.passphrase_protection = passphrase_protection
|
msg.passphrase_protection = passphrase_protection
|
||||||
msg.pin_protection = pin_protection
|
msg.pin_protection = pin_protection
|
||||||
|
25
python/src/trezorlib/messages.py
generated
25
python/src/trezorlib/messages.py
generated
@ -472,6 +472,13 @@ class HomescreenFormat(IntEnum):
|
|||||||
ToiG = 3
|
ToiG = 3
|
||||||
|
|
||||||
|
|
||||||
|
class RecoveryStatus(IntEnum):
|
||||||
|
NoRecovery = 0
|
||||||
|
InNormalRecovery = 1
|
||||||
|
InDryRunRecovery = 2
|
||||||
|
InUnlockRepeatedBackupRecovery = 3
|
||||||
|
|
||||||
|
|
||||||
class Capability(IntEnum):
|
class Capability(IntEnum):
|
||||||
Bitcoin = 1
|
Bitcoin = 1
|
||||||
Bitcoin_like = 2
|
Bitcoin_like = 2
|
||||||
@ -507,6 +514,12 @@ class RecoveryDeviceType(IntEnum):
|
|||||||
Matrix = 1
|
Matrix = 1
|
||||||
|
|
||||||
|
|
||||||
|
class RecoveryKind(IntEnum):
|
||||||
|
NormalRecovery = 0
|
||||||
|
DryRun = 1
|
||||||
|
UnlockRepeatedBackup = 2
|
||||||
|
|
||||||
|
|
||||||
class WordRequestType(IntEnum):
|
class WordRequestType(IntEnum):
|
||||||
Plain = 0
|
Plain = 0
|
||||||
Matrix9 = 1
|
Matrix9 = 1
|
||||||
@ -3241,7 +3254,7 @@ class Features(protobuf.MessageType):
|
|||||||
25: protobuf.Field("fw_vendor", "string", repeated=False, required=False, default=None),
|
25: protobuf.Field("fw_vendor", "string", repeated=False, required=False, default=None),
|
||||||
27: protobuf.Field("unfinished_backup", "bool", repeated=False, required=False, default=None),
|
27: protobuf.Field("unfinished_backup", "bool", repeated=False, required=False, default=None),
|
||||||
28: protobuf.Field("no_backup", "bool", repeated=False, required=False, default=None),
|
28: protobuf.Field("no_backup", "bool", repeated=False, required=False, default=None),
|
||||||
29: protobuf.Field("recovery_mode", "bool", repeated=False, required=False, default=None),
|
29: protobuf.Field("recovery_status", "RecoveryStatus", repeated=False, required=False, default=None),
|
||||||
30: protobuf.Field("capabilities", "Capability", repeated=True, required=False, default=None),
|
30: protobuf.Field("capabilities", "Capability", repeated=True, required=False, default=None),
|
||||||
31: protobuf.Field("backup_type", "BackupType", repeated=False, required=False, default=None),
|
31: protobuf.Field("backup_type", "BackupType", repeated=False, required=False, default=None),
|
||||||
32: protobuf.Field("sd_card_present", "bool", repeated=False, required=False, default=None),
|
32: protobuf.Field("sd_card_present", "bool", repeated=False, required=False, default=None),
|
||||||
@ -3297,7 +3310,7 @@ class Features(protobuf.MessageType):
|
|||||||
fw_vendor: Optional["str"] = None,
|
fw_vendor: Optional["str"] = None,
|
||||||
unfinished_backup: Optional["bool"] = None,
|
unfinished_backup: Optional["bool"] = None,
|
||||||
no_backup: Optional["bool"] = None,
|
no_backup: Optional["bool"] = None,
|
||||||
recovery_mode: Optional["bool"] = None,
|
recovery_status: Optional["RecoveryStatus"] = None,
|
||||||
backup_type: Optional["BackupType"] = None,
|
backup_type: Optional["BackupType"] = None,
|
||||||
sd_card_present: Optional["bool"] = None,
|
sd_card_present: Optional["bool"] = None,
|
||||||
sd_protection: Optional["bool"] = None,
|
sd_protection: Optional["bool"] = None,
|
||||||
@ -3348,7 +3361,7 @@ class Features(protobuf.MessageType):
|
|||||||
self.fw_vendor = fw_vendor
|
self.fw_vendor = fw_vendor
|
||||||
self.unfinished_backup = unfinished_backup
|
self.unfinished_backup = unfinished_backup
|
||||||
self.no_backup = no_backup
|
self.no_backup = no_backup
|
||||||
self.recovery_mode = recovery_mode
|
self.recovery_status = recovery_status
|
||||||
self.backup_type = backup_type
|
self.backup_type = backup_type
|
||||||
self.sd_card_present = sd_card_present
|
self.sd_card_present = sd_card_present
|
||||||
self.sd_protection = sd_protection
|
self.sd_protection = sd_protection
|
||||||
@ -3783,7 +3796,7 @@ class RecoveryDevice(protobuf.MessageType):
|
|||||||
6: protobuf.Field("enforce_wordlist", "bool", repeated=False, required=False, default=None),
|
6: protobuf.Field("enforce_wordlist", "bool", repeated=False, required=False, default=None),
|
||||||
8: protobuf.Field("type", "RecoveryDeviceType", repeated=False, required=False, default=None),
|
8: protobuf.Field("type", "RecoveryDeviceType", repeated=False, required=False, default=None),
|
||||||
9: protobuf.Field("u2f_counter", "uint32", repeated=False, required=False, default=None),
|
9: protobuf.Field("u2f_counter", "uint32", repeated=False, required=False, default=None),
|
||||||
10: protobuf.Field("dry_run", "bool", repeated=False, required=False, default=None),
|
10: protobuf.Field("kind", "RecoveryKind", repeated=False, required=False, default=RecoveryKind.NormalRecovery),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -3797,7 +3810,7 @@ class RecoveryDevice(protobuf.MessageType):
|
|||||||
enforce_wordlist: Optional["bool"] = None,
|
enforce_wordlist: Optional["bool"] = None,
|
||||||
type: Optional["RecoveryDeviceType"] = None,
|
type: Optional["RecoveryDeviceType"] = None,
|
||||||
u2f_counter: Optional["int"] = None,
|
u2f_counter: Optional["int"] = None,
|
||||||
dry_run: Optional["bool"] = None,
|
kind: Optional["RecoveryKind"] = RecoveryKind.NormalRecovery,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.word_count = word_count
|
self.word_count = word_count
|
||||||
self.passphrase_protection = passphrase_protection
|
self.passphrase_protection = passphrase_protection
|
||||||
@ -3807,7 +3820,7 @@ class RecoveryDevice(protobuf.MessageType):
|
|||||||
self.enforce_wordlist = enforce_wordlist
|
self.enforce_wordlist = enforce_wordlist
|
||||||
self.type = type
|
self.type = type
|
||||||
self.u2f_counter = u2f_counter
|
self.u2f_counter = u2f_counter
|
||||||
self.dry_run = dry_run
|
self.kind = kind
|
||||||
|
|
||||||
|
|
||||||
class WordRequest(protobuf.MessageType):
|
class WordRequest(protobuf.MessageType):
|
||||||
|
@ -169,7 +169,11 @@ impl Trezor {
|
|||||||
req.set_pin_protection(pin_protection);
|
req.set_pin_protection(pin_protection);
|
||||||
req.set_label(label);
|
req.set_label(label);
|
||||||
req.set_enforce_wordlist(true);
|
req.set_enforce_wordlist(true);
|
||||||
req.set_dry_run(dry_run);
|
if dry_run {
|
||||||
|
req.set_kind(protos::recovery_device::RecoveryKind::DryRun);
|
||||||
|
} else {
|
||||||
|
req.set_kind(protos::recovery_device::RecoveryKind::NormalRecovery);
|
||||||
|
}
|
||||||
req.set_type(
|
req.set_type(
|
||||||
protos::recovery_device::RecoveryDeviceType::RecoveryDeviceType_ScrambledWords,
|
protos::recovery_device::RecoveryDeviceType::RecoveryDeviceType_ScrambledWords,
|
||||||
);
|
);
|
||||||
|
@ -416,8 +416,8 @@ pub struct Features {
|
|||||||
pub unfinished_backup: ::std::option::Option<bool>,
|
pub unfinished_backup: ::std::option::Option<bool>,
|
||||||
// @@protoc_insertion_point(field:hw.trezor.messages.management.Features.no_backup)
|
// @@protoc_insertion_point(field:hw.trezor.messages.management.Features.no_backup)
|
||||||
pub no_backup: ::std::option::Option<bool>,
|
pub no_backup: ::std::option::Option<bool>,
|
||||||
// @@protoc_insertion_point(field:hw.trezor.messages.management.Features.recovery_mode)
|
// @@protoc_insertion_point(field:hw.trezor.messages.management.Features.recovery_status)
|
||||||
pub recovery_mode: ::std::option::Option<bool>,
|
pub recovery_status: ::std::option::Option<::protobuf::EnumOrUnknown<features::RecoveryStatus>>,
|
||||||
// @@protoc_insertion_point(field:hw.trezor.messages.management.Features.capabilities)
|
// @@protoc_insertion_point(field:hw.trezor.messages.management.Features.capabilities)
|
||||||
pub capabilities: ::std::vec::Vec<::protobuf::EnumOrUnknown<features::Capability>>,
|
pub capabilities: ::std::vec::Vec<::protobuf::EnumOrUnknown<features::Capability>>,
|
||||||
// @@protoc_insertion_point(field:hw.trezor.messages.management.Features.backup_type)
|
// @@protoc_insertion_point(field:hw.trezor.messages.management.Features.backup_type)
|
||||||
@ -1110,23 +1110,26 @@ impl Features {
|
|||||||
self.no_backup = ::std::option::Option::Some(v);
|
self.no_backup = ::std::option::Option::Some(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// optional bool recovery_mode = 29;
|
// optional .hw.trezor.messages.management.Features.RecoveryStatus recovery_status = 29;
|
||||||
|
|
||||||
pub fn recovery_mode(&self) -> bool {
|
pub fn recovery_status(&self) -> features::RecoveryStatus {
|
||||||
self.recovery_mode.unwrap_or(false)
|
match self.recovery_status {
|
||||||
|
Some(e) => e.enum_value_or(features::RecoveryStatus::NoRecovery),
|
||||||
|
None => features::RecoveryStatus::NoRecovery,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_recovery_mode(&mut self) {
|
pub fn clear_recovery_status(&mut self) {
|
||||||
self.recovery_mode = ::std::option::Option::None;
|
self.recovery_status = ::std::option::Option::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_recovery_mode(&self) -> bool {
|
pub fn has_recovery_status(&self) -> bool {
|
||||||
self.recovery_mode.is_some()
|
self.recovery_status.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Param is passed by value, moved
|
// Param is passed by value, moved
|
||||||
pub fn set_recovery_mode(&mut self, v: bool) {
|
pub fn set_recovery_status(&mut self, v: features::RecoveryStatus) {
|
||||||
self.recovery_mode = ::std::option::Option::Some(v);
|
self.recovery_status = ::std::option::Option::Some(::protobuf::EnumOrUnknown::new(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
// optional .hw.trezor.messages.management.BackupType backup_type = 31;
|
// optional .hw.trezor.messages.management.BackupType backup_type = 31;
|
||||||
@ -1724,9 +1727,9 @@ impl Features {
|
|||||||
|m: &mut Features| { &mut m.no_backup },
|
|m: &mut Features| { &mut m.no_backup },
|
||||||
));
|
));
|
||||||
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
|
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
|
||||||
"recovery_mode",
|
"recovery_status",
|
||||||
|m: &Features| { &m.recovery_mode },
|
|m: &Features| { &m.recovery_status },
|
||||||
|m: &mut Features| { &mut m.recovery_mode },
|
|m: &mut Features| { &mut m.recovery_status },
|
||||||
));
|
));
|
||||||
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
|
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
|
||||||
"capabilities",
|
"capabilities",
|
||||||
@ -1949,7 +1952,7 @@ impl ::protobuf::Message for Features {
|
|||||||
self.no_backup = ::std::option::Option::Some(is.read_bool()?);
|
self.no_backup = ::std::option::Option::Some(is.read_bool()?);
|
||||||
},
|
},
|
||||||
232 => {
|
232 => {
|
||||||
self.recovery_mode = ::std::option::Option::Some(is.read_bool()?);
|
self.recovery_status = ::std::option::Option::Some(is.read_enum_or_unknown()?);
|
||||||
},
|
},
|
||||||
240 => {
|
240 => {
|
||||||
self.capabilities.push(is.read_enum_or_unknown()?);
|
self.capabilities.push(is.read_enum_or_unknown()?);
|
||||||
@ -2113,8 +2116,8 @@ impl ::protobuf::Message for Features {
|
|||||||
if let Some(v) = self.no_backup {
|
if let Some(v) = self.no_backup {
|
||||||
my_size += 2 + 1;
|
my_size += 2 + 1;
|
||||||
}
|
}
|
||||||
if let Some(v) = self.recovery_mode {
|
if let Some(v) = self.recovery_status {
|
||||||
my_size += 2 + 1;
|
my_size += ::protobuf::rt::int32_size(29, v.value());
|
||||||
}
|
}
|
||||||
for value in &self.capabilities {
|
for value in &self.capabilities {
|
||||||
my_size += ::protobuf::rt::int32_size(30, value.value());
|
my_size += ::protobuf::rt::int32_size(30, value.value());
|
||||||
@ -2269,8 +2272,8 @@ impl ::protobuf::Message for Features {
|
|||||||
if let Some(v) = self.no_backup {
|
if let Some(v) = self.no_backup {
|
||||||
os.write_bool(28, v)?;
|
os.write_bool(28, v)?;
|
||||||
}
|
}
|
||||||
if let Some(v) = self.recovery_mode {
|
if let Some(v) = self.recovery_status {
|
||||||
os.write_bool(29, v)?;
|
os.write_enum(29, ::protobuf::EnumOrUnknown::value(&v))?;
|
||||||
}
|
}
|
||||||
for v in &self.capabilities {
|
for v in &self.capabilities {
|
||||||
os.write_enum(30, ::protobuf::EnumOrUnknown::value(v))?;
|
os.write_enum(30, ::protobuf::EnumOrUnknown::value(v))?;
|
||||||
@ -2384,7 +2387,7 @@ impl ::protobuf::Message for Features {
|
|||||||
self.fw_vendor = ::std::option::Option::None;
|
self.fw_vendor = ::std::option::Option::None;
|
||||||
self.unfinished_backup = ::std::option::Option::None;
|
self.unfinished_backup = ::std::option::Option::None;
|
||||||
self.no_backup = ::std::option::Option::None;
|
self.no_backup = ::std::option::Option::None;
|
||||||
self.recovery_mode = ::std::option::Option::None;
|
self.recovery_status = ::std::option::Option::None;
|
||||||
self.capabilities.clear();
|
self.capabilities.clear();
|
||||||
self.backup_type = ::std::option::Option::None;
|
self.backup_type = ::std::option::Option::None;
|
||||||
self.sd_card_present = ::std::option::Option::None;
|
self.sd_card_present = ::std::option::Option::None;
|
||||||
@ -2439,7 +2442,7 @@ impl ::protobuf::Message for Features {
|
|||||||
fw_vendor: ::std::option::Option::None,
|
fw_vendor: ::std::option::Option::None,
|
||||||
unfinished_backup: ::std::option::Option::None,
|
unfinished_backup: ::std::option::Option::None,
|
||||||
no_backup: ::std::option::Option::None,
|
no_backup: ::std::option::Option::None,
|
||||||
recovery_mode: ::std::option::Option::None,
|
recovery_status: ::std::option::Option::None,
|
||||||
capabilities: ::std::vec::Vec::new(),
|
capabilities: ::std::vec::Vec::new(),
|
||||||
backup_type: ::std::option::Option::None,
|
backup_type: ::std::option::Option::None,
|
||||||
sd_card_present: ::std::option::Option::None,
|
sd_card_present: ::std::option::Option::None,
|
||||||
@ -2488,6 +2491,78 @@ impl ::protobuf::reflect::ProtobufValue for Features {
|
|||||||
|
|
||||||
/// Nested message and enums of message `Features`
|
/// Nested message and enums of message `Features`
|
||||||
pub mod features {
|
pub mod features {
|
||||||
|
#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
|
||||||
|
// @@protoc_insertion_point(enum:hw.trezor.messages.management.Features.RecoveryStatus)
|
||||||
|
pub enum RecoveryStatus {
|
||||||
|
// @@protoc_insertion_point(enum_value:hw.trezor.messages.management.Features.RecoveryStatus.NoRecovery)
|
||||||
|
NoRecovery = 0,
|
||||||
|
// @@protoc_insertion_point(enum_value:hw.trezor.messages.management.Features.RecoveryStatus.InNormalRecovery)
|
||||||
|
InNormalRecovery = 1,
|
||||||
|
// @@protoc_insertion_point(enum_value:hw.trezor.messages.management.Features.RecoveryStatus.InDryRunRecovery)
|
||||||
|
InDryRunRecovery = 2,
|
||||||
|
// @@protoc_insertion_point(enum_value:hw.trezor.messages.management.Features.RecoveryStatus.InUnlockRepeatedBackupRecovery)
|
||||||
|
InUnlockRepeatedBackupRecovery = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::protobuf::Enum for RecoveryStatus {
|
||||||
|
const NAME: &'static str = "RecoveryStatus";
|
||||||
|
|
||||||
|
fn value(&self) -> i32 {
|
||||||
|
*self as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_i32(value: i32) -> ::std::option::Option<RecoveryStatus> {
|
||||||
|
match value {
|
||||||
|
0 => ::std::option::Option::Some(RecoveryStatus::NoRecovery),
|
||||||
|
1 => ::std::option::Option::Some(RecoveryStatus::InNormalRecovery),
|
||||||
|
2 => ::std::option::Option::Some(RecoveryStatus::InDryRunRecovery),
|
||||||
|
3 => ::std::option::Option::Some(RecoveryStatus::InUnlockRepeatedBackupRecovery),
|
||||||
|
_ => ::std::option::Option::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_str(str: &str) -> ::std::option::Option<RecoveryStatus> {
|
||||||
|
match str {
|
||||||
|
"NoRecovery" => ::std::option::Option::Some(RecoveryStatus::NoRecovery),
|
||||||
|
"InNormalRecovery" => ::std::option::Option::Some(RecoveryStatus::InNormalRecovery),
|
||||||
|
"InDryRunRecovery" => ::std::option::Option::Some(RecoveryStatus::InDryRunRecovery),
|
||||||
|
"InUnlockRepeatedBackupRecovery" => ::std::option::Option::Some(RecoveryStatus::InUnlockRepeatedBackupRecovery),
|
||||||
|
_ => ::std::option::Option::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VALUES: &'static [RecoveryStatus] = &[
|
||||||
|
RecoveryStatus::NoRecovery,
|
||||||
|
RecoveryStatus::InNormalRecovery,
|
||||||
|
RecoveryStatus::InDryRunRecovery,
|
||||||
|
RecoveryStatus::InUnlockRepeatedBackupRecovery,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::protobuf::EnumFull for RecoveryStatus {
|
||||||
|
fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor {
|
||||||
|
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new();
|
||||||
|
descriptor.get(|| super::file_descriptor().enum_by_package_relative_name("Features.RecoveryStatus").unwrap()).clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor {
|
||||||
|
let index = *self as usize;
|
||||||
|
Self::enum_descriptor().value_by_index(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::default::Default for RecoveryStatus {
|
||||||
|
fn default() -> Self {
|
||||||
|
RecoveryStatus::NoRecovery
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecoveryStatus {
|
||||||
|
pub(in super) fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData {
|
||||||
|
::protobuf::reflect::GeneratedEnumDescriptorData::new::<RecoveryStatus>("Features.RecoveryStatus")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
|
#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
|
||||||
// @@protoc_insertion_point(enum:hw.trezor.messages.management.Features.Capability)
|
// @@protoc_insertion_point(enum:hw.trezor.messages.management.Features.Capability)
|
||||||
pub enum Capability {
|
pub enum Capability {
|
||||||
@ -7768,8 +7843,8 @@ pub struct RecoveryDevice {
|
|||||||
pub type_: ::std::option::Option<::protobuf::EnumOrUnknown<recovery_device::RecoveryDeviceType>>,
|
pub type_: ::std::option::Option<::protobuf::EnumOrUnknown<recovery_device::RecoveryDeviceType>>,
|
||||||
// @@protoc_insertion_point(field:hw.trezor.messages.management.RecoveryDevice.u2f_counter)
|
// @@protoc_insertion_point(field:hw.trezor.messages.management.RecoveryDevice.u2f_counter)
|
||||||
pub u2f_counter: ::std::option::Option<u32>,
|
pub u2f_counter: ::std::option::Option<u32>,
|
||||||
// @@protoc_insertion_point(field:hw.trezor.messages.management.RecoveryDevice.dry_run)
|
// @@protoc_insertion_point(field:hw.trezor.messages.management.RecoveryDevice.kind)
|
||||||
pub dry_run: ::std::option::Option<bool>,
|
pub kind: ::std::option::Option<::protobuf::EnumOrUnknown<recovery_device::RecoveryKind>>,
|
||||||
// special fields
|
// special fields
|
||||||
// @@protoc_insertion_point(special_field:hw.trezor.messages.management.RecoveryDevice.special_fields)
|
// @@protoc_insertion_point(special_field:hw.trezor.messages.management.RecoveryDevice.special_fields)
|
||||||
pub special_fields: ::protobuf::SpecialFields,
|
pub special_fields: ::protobuf::SpecialFields,
|
||||||
@ -7975,23 +8050,26 @@ impl RecoveryDevice {
|
|||||||
self.u2f_counter = ::std::option::Option::Some(v);
|
self.u2f_counter = ::std::option::Option::Some(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// optional bool dry_run = 10;
|
// optional .hw.trezor.messages.management.RecoveryDevice.RecoveryKind kind = 10;
|
||||||
|
|
||||||
pub fn dry_run(&self) -> bool {
|
pub fn kind(&self) -> recovery_device::RecoveryKind {
|
||||||
self.dry_run.unwrap_or(false)
|
match self.kind {
|
||||||
|
Some(e) => e.enum_value_or(recovery_device::RecoveryKind::NormalRecovery),
|
||||||
|
None => recovery_device::RecoveryKind::NormalRecovery,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_dry_run(&mut self) {
|
pub fn clear_kind(&mut self) {
|
||||||
self.dry_run = ::std::option::Option::None;
|
self.kind = ::std::option::Option::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_dry_run(&self) -> bool {
|
pub fn has_kind(&self) -> bool {
|
||||||
self.dry_run.is_some()
|
self.kind.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Param is passed by value, moved
|
// Param is passed by value, moved
|
||||||
pub fn set_dry_run(&mut self, v: bool) {
|
pub fn set_kind(&mut self, v: recovery_device::RecoveryKind) {
|
||||||
self.dry_run = ::std::option::Option::Some(v);
|
self.kind = ::std::option::Option::Some(::protobuf::EnumOrUnknown::new(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||||
@ -8038,9 +8116,9 @@ impl RecoveryDevice {
|
|||||||
|m: &mut RecoveryDevice| { &mut m.u2f_counter },
|
|m: &mut RecoveryDevice| { &mut m.u2f_counter },
|
||||||
));
|
));
|
||||||
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
|
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
|
||||||
"dry_run",
|
"kind",
|
||||||
|m: &RecoveryDevice| { &m.dry_run },
|
|m: &RecoveryDevice| { &m.kind },
|
||||||
|m: &mut RecoveryDevice| { &mut m.dry_run },
|
|m: &mut RecoveryDevice| { &mut m.kind },
|
||||||
));
|
));
|
||||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<RecoveryDevice>(
|
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<RecoveryDevice>(
|
||||||
"RecoveryDevice",
|
"RecoveryDevice",
|
||||||
@ -8085,7 +8163,7 @@ impl ::protobuf::Message for RecoveryDevice {
|
|||||||
self.u2f_counter = ::std::option::Option::Some(is.read_uint32()?);
|
self.u2f_counter = ::std::option::Option::Some(is.read_uint32()?);
|
||||||
},
|
},
|
||||||
80 => {
|
80 => {
|
||||||
self.dry_run = ::std::option::Option::Some(is.read_bool()?);
|
self.kind = ::std::option::Option::Some(is.read_enum_or_unknown()?);
|
||||||
},
|
},
|
||||||
tag => {
|
tag => {
|
||||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||||
@ -8123,8 +8201,8 @@ impl ::protobuf::Message for RecoveryDevice {
|
|||||||
if let Some(v) = self.u2f_counter {
|
if let Some(v) = self.u2f_counter {
|
||||||
my_size += ::protobuf::rt::uint32_size(9, v);
|
my_size += ::protobuf::rt::uint32_size(9, v);
|
||||||
}
|
}
|
||||||
if let Some(v) = self.dry_run {
|
if let Some(v) = self.kind {
|
||||||
my_size += 1 + 1;
|
my_size += ::protobuf::rt::int32_size(10, v.value());
|
||||||
}
|
}
|
||||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||||
self.special_fields.cached_size().set(my_size as u32);
|
self.special_fields.cached_size().set(my_size as u32);
|
||||||
@ -8156,8 +8234,8 @@ impl ::protobuf::Message for RecoveryDevice {
|
|||||||
if let Some(v) = self.u2f_counter {
|
if let Some(v) = self.u2f_counter {
|
||||||
os.write_uint32(9, v)?;
|
os.write_uint32(9, v)?;
|
||||||
}
|
}
|
||||||
if let Some(v) = self.dry_run {
|
if let Some(v) = self.kind {
|
||||||
os.write_bool(10, v)?;
|
os.write_enum(10, ::protobuf::EnumOrUnknown::value(&v))?;
|
||||||
}
|
}
|
||||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||||
::std::result::Result::Ok(())
|
::std::result::Result::Ok(())
|
||||||
@ -8184,7 +8262,7 @@ impl ::protobuf::Message for RecoveryDevice {
|
|||||||
self.enforce_wordlist = ::std::option::Option::None;
|
self.enforce_wordlist = ::std::option::Option::None;
|
||||||
self.type_ = ::std::option::Option::None;
|
self.type_ = ::std::option::Option::None;
|
||||||
self.u2f_counter = ::std::option::Option::None;
|
self.u2f_counter = ::std::option::Option::None;
|
||||||
self.dry_run = ::std::option::Option::None;
|
self.kind = ::std::option::Option::None;
|
||||||
self.special_fields.clear();
|
self.special_fields.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8198,7 +8276,7 @@ impl ::protobuf::Message for RecoveryDevice {
|
|||||||
enforce_wordlist: ::std::option::Option::None,
|
enforce_wordlist: ::std::option::Option::None,
|
||||||
type_: ::std::option::Option::None,
|
type_: ::std::option::Option::None,
|
||||||
u2f_counter: ::std::option::Option::None,
|
u2f_counter: ::std::option::Option::None,
|
||||||
dry_run: ::std::option::Option::None,
|
kind: ::std::option::Option::None,
|
||||||
special_fields: ::protobuf::SpecialFields::new(),
|
special_fields: ::protobuf::SpecialFields::new(),
|
||||||
};
|
};
|
||||||
&instance
|
&instance
|
||||||
@ -8285,6 +8363,73 @@ pub mod recovery_device {
|
|||||||
::protobuf::reflect::GeneratedEnumDescriptorData::new::<RecoveryDeviceType>("RecoveryDevice.RecoveryDeviceType")
|
::protobuf::reflect::GeneratedEnumDescriptorData::new::<RecoveryDeviceType>("RecoveryDevice.RecoveryDeviceType")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
|
||||||
|
// @@protoc_insertion_point(enum:hw.trezor.messages.management.RecoveryDevice.RecoveryKind)
|
||||||
|
pub enum RecoveryKind {
|
||||||
|
// @@protoc_insertion_point(enum_value:hw.trezor.messages.management.RecoveryDevice.RecoveryKind.NormalRecovery)
|
||||||
|
NormalRecovery = 0,
|
||||||
|
// @@protoc_insertion_point(enum_value:hw.trezor.messages.management.RecoveryDevice.RecoveryKind.DryRun)
|
||||||
|
DryRun = 1,
|
||||||
|
// @@protoc_insertion_point(enum_value:hw.trezor.messages.management.RecoveryDevice.RecoveryKind.UnlockRepeatedBackup)
|
||||||
|
UnlockRepeatedBackup = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::protobuf::Enum for RecoveryKind {
|
||||||
|
const NAME: &'static str = "RecoveryKind";
|
||||||
|
|
||||||
|
fn value(&self) -> i32 {
|
||||||
|
*self as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_i32(value: i32) -> ::std::option::Option<RecoveryKind> {
|
||||||
|
match value {
|
||||||
|
0 => ::std::option::Option::Some(RecoveryKind::NormalRecovery),
|
||||||
|
1 => ::std::option::Option::Some(RecoveryKind::DryRun),
|
||||||
|
2 => ::std::option::Option::Some(RecoveryKind::UnlockRepeatedBackup),
|
||||||
|
_ => ::std::option::Option::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_str(str: &str) -> ::std::option::Option<RecoveryKind> {
|
||||||
|
match str {
|
||||||
|
"NormalRecovery" => ::std::option::Option::Some(RecoveryKind::NormalRecovery),
|
||||||
|
"DryRun" => ::std::option::Option::Some(RecoveryKind::DryRun),
|
||||||
|
"UnlockRepeatedBackup" => ::std::option::Option::Some(RecoveryKind::UnlockRepeatedBackup),
|
||||||
|
_ => ::std::option::Option::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VALUES: &'static [RecoveryKind] = &[
|
||||||
|
RecoveryKind::NormalRecovery,
|
||||||
|
RecoveryKind::DryRun,
|
||||||
|
RecoveryKind::UnlockRepeatedBackup,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::protobuf::EnumFull for RecoveryKind {
|
||||||
|
fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor {
|
||||||
|
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new();
|
||||||
|
descriptor.get(|| super::file_descriptor().enum_by_package_relative_name("RecoveryDevice.RecoveryKind").unwrap()).clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor {
|
||||||
|
let index = *self as usize;
|
||||||
|
Self::enum_descriptor().value_by_index(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::default::Default for RecoveryKind {
|
||||||
|
fn default() -> Self {
|
||||||
|
RecoveryKind::NormalRecovery
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecoveryKind {
|
||||||
|
pub(in super) fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData {
|
||||||
|
::protobuf::reflect::GeneratedEnumDescriptorData::new::<RecoveryKind>("RecoveryDevice.RecoveryKind")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @@protoc_insertion_point(message:hw.trezor.messages.management.WordRequest)
|
// @@protoc_insertion_point(message:hw.trezor.messages.management.WordRequest)
|
||||||
@ -10839,7 +10984,7 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
|||||||
\x0emessages.proto\"\x80\x01\n\nInitialize\x12\x1d\n\nsession_id\x18\x01\
|
\x0emessages.proto\"\x80\x01\n\nInitialize\x12\x1d\n\nsession_id\x18\x01\
|
||||||
\x20\x01(\x0cR\tsessionId\x12,\n\x10_skip_passphrase\x18\x02\x20\x01(\
|
\x20\x01(\x0cR\tsessionId\x12,\n\x10_skip_passphrase\x18\x02\x20\x01(\
|
||||||
\x08R\x0eSkipPassphraseB\x02\x18\x01\x12%\n\x0ederive_cardano\x18\x03\
|
\x08R\x0eSkipPassphraseB\x02\x18\x01\x12%\n\x0ederive_cardano\x18\x03\
|
||||||
\x20\x01(\x08R\rderiveCardano\"\r\n\x0bGetFeatures\"\xe1\x14\n\x08Featur\
|
\x20\x01(\x08R\rderiveCardano\"\r\n\x0bGetFeatures\"\x8f\x16\n\x08Featur\
|
||||||
es\x12\x16\n\x06vendor\x18\x01\x20\x01(\tR\x06vendor\x12#\n\rmajor_versi\
|
es\x12\x16\n\x06vendor\x18\x01\x20\x01(\tR\x06vendor\x12#\n\rmajor_versi\
|
||||||
on\x18\x02\x20\x02(\rR\x0cmajorVersion\x12#\n\rminor_version\x18\x03\x20\
|
on\x18\x02\x20\x02(\rR\x0cmajorVersion\x12#\n\rminor_version\x18\x03\x20\
|
||||||
\x02(\rR\x0cminorVersion\x12#\n\rpatch_version\x18\x04\x20\x02(\rR\x0cpa\
|
\x02(\rR\x0cminorVersion\x12#\n\rpatch_version\x18\x04\x20\x02(\rR\x0cpa\
|
||||||
@ -10861,22 +11006,23 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
|||||||
\x07fwMinor\x12\x19\n\x08fw_patch\x18\x18\x20\x01(\rR\x07fwPatch\x12\x1b\
|
\x07fwMinor\x12\x19\n\x08fw_patch\x18\x18\x20\x01(\rR\x07fwPatch\x12\x1b\
|
||||||
\n\tfw_vendor\x18\x19\x20\x01(\tR\x08fwVendor\x12+\n\x11unfinished_backu\
|
\n\tfw_vendor\x18\x19\x20\x01(\tR\x08fwVendor\x12+\n\x11unfinished_backu\
|
||||||
p\x18\x1b\x20\x01(\x08R\x10unfinishedBackup\x12\x1b\n\tno_backup\x18\x1c\
|
p\x18\x1b\x20\x01(\x08R\x10unfinishedBackup\x12\x1b\n\tno_backup\x18\x1c\
|
||||||
\x20\x01(\x08R\x08noBackup\x12#\n\rrecovery_mode\x18\x1d\x20\x01(\x08R\
|
\x20\x01(\x08R\x08noBackup\x12_\n\x0frecovery_status\x18\x1d\x20\x01(\
|
||||||
\x0crecoveryMode\x12V\n\x0ccapabilities\x18\x1e\x20\x03(\x0e22.hw.trezor\
|
\x0e26.hw.trezor.messages.management.Features.RecoveryStatusR\x0erecover\
|
||||||
.messages.management.Features.CapabilityR\x0ccapabilities\x12J\n\x0bback\
|
yStatus\x12V\n\x0ccapabilities\x18\x1e\x20\x03(\x0e22.hw.trezor.messages\
|
||||||
up_type\x18\x1f\x20\x01(\x0e2).hw.trezor.messages.management.BackupTypeR\
|
.management.Features.CapabilityR\x0ccapabilities\x12J\n\x0bbackup_type\
|
||||||
\nbackupType\x12&\n\x0fsd_card_present\x18\x20\x20\x01(\x08R\rsdCardPres\
|
\x18\x1f\x20\x01(\x0e2).hw.trezor.messages.management.BackupTypeR\nbacku\
|
||||||
ent\x12#\n\rsd_protection\x18!\x20\x01(\x08R\x0csdProtection\x120\n\x14w\
|
pType\x12&\n\x0fsd_card_present\x18\x20\x20\x01(\x08R\rsdCardPresent\x12\
|
||||||
ipe_code_protection\x18\"\x20\x01(\x08R\x12wipeCodeProtection\x12\x1d\n\
|
#\n\rsd_protection\x18!\x20\x01(\x08R\x0csdProtection\x120\n\x14wipe_cod\
|
||||||
\nsession_id\x18#\x20\x01(\x0cR\tsessionId\x12=\n\x1bpassphrase_always_o\
|
e_protection\x18\"\x20\x01(\x08R\x12wipeCodeProtection\x12\x1d\n\nsessio\
|
||||||
n_device\x18$\x20\x01(\x08R\x18passphraseAlwaysOnDevice\x12T\n\rsafety_c\
|
n_id\x18#\x20\x01(\x0cR\tsessionId\x12=\n\x1bpassphrase_always_on_device\
|
||||||
hecks\x18%\x20\x01(\x0e2/.hw.trezor.messages.management.SafetyCheckLevel\
|
\x18$\x20\x01(\x08R\x18passphraseAlwaysOnDevice\x12T\n\rsafety_checks\
|
||||||
R\x0csafetyChecks\x12+\n\x12auto_lock_delay_ms\x18&\x20\x01(\rR\x0fautoL\
|
\x18%\x20\x01(\x0e2/.hw.trezor.messages.management.SafetyCheckLevelR\x0c\
|
||||||
ockDelayMs\x12)\n\x10display_rotation\x18'\x20\x01(\rR\x0fdisplayRotatio\
|
safetyChecks\x12+\n\x12auto_lock_delay_ms\x18&\x20\x01(\rR\x0fautoLockDe\
|
||||||
n\x123\n\x15experimental_features\x18(\x20\x01(\x08R\x14experimentalFeat\
|
layMs\x12)\n\x10display_rotation\x18'\x20\x01(\rR\x0fdisplayRotation\x12\
|
||||||
ures\x12\x12\n\x04busy\x18)\x20\x01(\x08R\x04busy\x12\\\n\x11homescreen_\
|
3\n\x15experimental_features\x18(\x20\x01(\x08R\x14experimentalFeatures\
|
||||||
format\x18*\x20\x01(\x0e2/.hw.trezor.messages.management.HomescreenForma\
|
\x12\x12\n\x04busy\x18)\x20\x01(\x08R\x04busy\x12\\\n\x11homescreen_form\
|
||||||
tR\x10homescreenFormat\x129\n\x19hide_passphrase_from_host\x18+\x20\x01(\
|
at\x18*\x20\x01(\x0e2/.hw.trezor.messages.management.HomescreenFormatR\
|
||||||
|
\x10homescreenFormat\x129\n\x19hide_passphrase_from_host\x18+\x20\x01(\
|
||||||
\x08R\x16hidePassphraseFromHost\x12%\n\x0einternal_model\x18,\x20\x01(\t\
|
\x08R\x16hidePassphraseFromHost\x12%\n\x0einternal_model\x18,\x20\x01(\t\
|
||||||
R\rinternalModel\x12\x1d\n\nunit_color\x18-\x20\x01(\rR\tunitColor\x12!\
|
R\rinternalModel\x12\x1d\n\nunit_color\x18-\x20\x01(\rR\tunitColor\x12!\
|
||||||
\n\x0cunit_btconly\x18.\x20\x01(\x08R\x0bunitBtconly\x12)\n\x10homescree\
|
\n\x0cunit_btconly\x18.\x20\x01(\x08R\x0bunitBtconly\x12)\n\x10homescree\
|
||||||
@ -10885,120 +11031,125 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
|||||||
\x20\x01(\x08R\x10bootloaderLocked\x12>\n\x18language_version_matches\
|
\x20\x01(\x08R\x10bootloaderLocked\x12>\n\x18language_version_matches\
|
||||||
\x182\x20\x01(\x08:\x04trueR\x16languageVersionMatches\x12%\n\x0eunit_pa\
|
\x182\x20\x01(\x08:\x04trueR\x16languageVersionMatches\x12%\n\x0eunit_pa\
|
||||||
ckaging\x183\x20\x01(\rR\runitPackaging\x12'\n\x0fhaptic_feedback\x184\
|
ckaging\x183\x20\x01(\rR\runitPackaging\x12'\n\x0fhaptic_feedback\x184\
|
||||||
\x20\x01(\x08R\x0ehapticFeedback\"\xc2\x04\n\nCapability\x12\x1c\n\x12Ca\
|
\x20\x01(\x08R\x0ehapticFeedback\"p\n\x0eRecoveryStatus\x12\x0e\n\nNoRec\
|
||||||
pability_Bitcoin\x10\x01\x1a\x04\x80\xa6\x1d\x01\x12\x1b\n\x17Capability\
|
overy\x10\0\x12\x14\n\x10InNormalRecovery\x10\x01\x12\x14\n\x10InDryRunR\
|
||||||
_Bitcoin_like\x10\x02\x12\x16\n\x12Capability_Binance\x10\x03\x12\x16\n\
|
ecovery\x10\x02\x12\"\n\x1eInUnlockRepeatedBackupRecovery\x10\x03\"\xc2\
|
||||||
\x12Capability_Cardano\x10\x04\x12\x1b\n\x11Capability_Crypto\x10\x05\
|
\x04\n\nCapability\x12\x1c\n\x12Capability_Bitcoin\x10\x01\x1a\x04\x80\
|
||||||
\x1a\x04\x80\xa6\x1d\x01\x12\x12\n\x0eCapability_EOS\x10\x06\x12\x17\n\
|
\xa6\x1d\x01\x12\x1b\n\x17Capability_Bitcoin_like\x10\x02\x12\x16\n\x12C\
|
||||||
\x13Capability_Ethereum\x10\x07\x12\x17\n\x0fCapability_Lisk\x10\x08\x1a\
|
apability_Binance\x10\x03\x12\x16\n\x12Capability_Cardano\x10\x04\x12\
|
||||||
\x02\x08\x01\x12\x15\n\x11Capability_Monero\x10\t\x12\x12\n\x0eCapabilit\
|
\x1b\n\x11Capability_Crypto\x10\x05\x1a\x04\x80\xa6\x1d\x01\x12\x12\n\
|
||||||
y_NEM\x10\n\x12\x15\n\x11Capability_Ripple\x10\x0b\x12\x16\n\x12Capabili\
|
\x0eCapability_EOS\x10\x06\x12\x17\n\x13Capability_Ethereum\x10\x07\x12\
|
||||||
ty_Stellar\x10\x0c\x12\x14\n\x10Capability_Tezos\x10\r\x12\x12\n\x0eCapa\
|
\x17\n\x0fCapability_Lisk\x10\x08\x1a\x02\x08\x01\x12\x15\n\x11Capabilit\
|
||||||
bility_U2F\x10\x0e\x12\x1b\n\x11Capability_Shamir\x10\x0f\x1a\x04\x80\
|
y_Monero\x10\t\x12\x12\n\x0eCapability_NEM\x10\n\x12\x15\n\x11Capability\
|
||||||
\xa6\x1d\x01\x12!\n\x17Capability_ShamirGroups\x10\x10\x1a\x04\x80\xa6\
|
_Ripple\x10\x0b\x12\x16\n\x12Capability_Stellar\x10\x0c\x12\x14\n\x10Cap\
|
||||||
\x1d\x01\x12$\n\x1aCapability_PassphraseEntry\x10\x11\x1a\x04\x80\xa6\
|
ability_Tezos\x10\r\x12\x12\n\x0eCapability_U2F\x10\x0e\x12\x1b\n\x11Cap\
|
||||||
\x1d\x01\x12\x15\n\x11Capability_Solana\x10\x12\x12!\n\x17Capability_Tra\
|
ability_Shamir\x10\x0f\x1a\x04\x80\xa6\x1d\x01\x12!\n\x17Capability_Sham\
|
||||||
nslations\x10\x13\x1a\x04\x80\xa6\x1d\x01\x12\x1f\n\x15Capability_Bright\
|
irGroups\x10\x10\x1a\x04\x80\xa6\x1d\x01\x12$\n\x1aCapability_Passphrase\
|
||||||
ness\x10\x14\x1a\x04\x80\xa6\x1d\x01\x12\x1b\n\x11Capability_Haptic\x10\
|
Entry\x10\x11\x1a\x04\x80\xa6\x1d\x01\x12\x15\n\x11Capability_Solana\x10\
|
||||||
\x15\x1a\x04\x80\xa6\x1d\x01\x1a\x04\xc8\xf3\x18\x01\"\x0c\n\nLockDevice\
|
\x12\x12!\n\x17Capability_Translations\x10\x13\x1a\x04\x80\xa6\x1d\x01\
|
||||||
\"&\n\x07SetBusy\x12\x1b\n\texpiry_ms\x18\x01\x20\x01(\rR\x08expiryMs\"\
|
\x12\x1f\n\x15Capability_Brightness\x10\x14\x1a\x04\x80\xa6\x1d\x01\x12\
|
||||||
\x0c\n\nEndSession\"\xc4\x04\n\rApplySettings\x12\x1e\n\x08language\x18\
|
\x1b\n\x11Capability_Haptic\x10\x15\x1a\x04\x80\xa6\x1d\x01\x1a\x04\xc8\
|
||||||
\x01\x20\x01(\tR\x08languageB\x02\x18\x01\x12\x14\n\x05label\x18\x02\x20\
|
\xf3\x18\x01\"\x0c\n\nLockDevice\"&\n\x07SetBusy\x12\x1b\n\texpiry_ms\
|
||||||
\x01(\tR\x05label\x12%\n\x0euse_passphrase\x18\x03\x20\x01(\x08R\rusePas\
|
\x18\x01\x20\x01(\rR\x08expiryMs\"\x0c\n\nEndSession\"\xc4\x04\n\rApplyS\
|
||||||
sphrase\x12\x1e\n\nhomescreen\x18\x04\x20\x01(\x0cR\nhomescreen\x120\n\
|
ettings\x12\x1e\n\x08language\x18\x01\x20\x01(\tR\x08languageB\x02\x18\
|
||||||
\x12_passphrase_source\x18\x05\x20\x01(\rR\x10PassphraseSourceB\x02\x18\
|
\x01\x12\x14\n\x05label\x18\x02\x20\x01(\tR\x05label\x12%\n\x0euse_passp\
|
||||||
\x01\x12+\n\x12auto_lock_delay_ms\x18\x06\x20\x01(\rR\x0fautoLockDelayMs\
|
hrase\x18\x03\x20\x01(\x08R\rusePassphrase\x12\x1e\n\nhomescreen\x18\x04\
|
||||||
\x12)\n\x10display_rotation\x18\x07\x20\x01(\rR\x0fdisplayRotation\x12=\
|
\x20\x01(\x0cR\nhomescreen\x120\n\x12_passphrase_source\x18\x05\x20\x01(\
|
||||||
\n\x1bpassphrase_always_on_device\x18\x08\x20\x01(\x08R\x18passphraseAlw\
|
\rR\x10PassphraseSourceB\x02\x18\x01\x12+\n\x12auto_lock_delay_ms\x18\
|
||||||
aysOnDevice\x12T\n\rsafety_checks\x18\t\x20\x01(\x0e2/.hw.trezor.message\
|
\x06\x20\x01(\rR\x0fautoLockDelayMs\x12)\n\x10display_rotation\x18\x07\
|
||||||
s.management.SafetyCheckLevelR\x0csafetyChecks\x123\n\x15experimental_fe\
|
\x20\x01(\rR\x0fdisplayRotation\x12=\n\x1bpassphrase_always_on_device\
|
||||||
atures\x18\n\x20\x01(\x08R\x14experimentalFeatures\x129\n\x19hide_passph\
|
\x18\x08\x20\x01(\x08R\x18passphraseAlwaysOnDevice\x12T\n\rsafety_checks\
|
||||||
rase_from_host\x18\x0b\x20\x01(\x08R\x16hidePassphraseFromHost\x12'\n\
|
\x18\t\x20\x01(\x0e2/.hw.trezor.messages.management.SafetyCheckLevelR\
|
||||||
\x0fhaptic_feedback\x18\r\x20\x01(\x08R\x0ehapticFeedback\"T\n\x0eChange\
|
\x0csafetyChecks\x123\n\x15experimental_features\x18\n\x20\x01(\x08R\x14\
|
||||||
Language\x12\x1f\n\x0bdata_length\x18\x01\x20\x02(\rR\ndataLength\x12!\n\
|
experimentalFeatures\x129\n\x19hide_passphrase_from_host\x18\x0b\x20\x01\
|
||||||
\x0cshow_display\x18\x02\x20\x01(\x08R\x0bshowDisplay\"Z\n\x16Translatio\
|
(\x08R\x16hidePassphraseFromHost\x12'\n\x0fhaptic_feedback\x18\r\x20\x01\
|
||||||
nDataRequest\x12\x1f\n\x0bdata_length\x18\x01\x20\x02(\rR\ndataLength\
|
(\x08R\x0ehapticFeedback\"T\n\x0eChangeLanguage\x12\x1f\n\x0bdata_length\
|
||||||
\x12\x1f\n\x0bdata_offset\x18\x02\x20\x02(\rR\ndataOffset\"3\n\x12Transl\
|
\x18\x01\x20\x02(\rR\ndataLength\x12!\n\x0cshow_display\x18\x02\x20\x01(\
|
||||||
ationDataAck\x12\x1d\n\ndata_chunk\x18\x01\x20\x02(\x0cR\tdataChunk\"\"\
|
\x08R\x0bshowDisplay\"Z\n\x16TranslationDataRequest\x12\x1f\n\x0bdata_le\
|
||||||
\n\nApplyFlags\x12\x14\n\x05flags\x18\x01\x20\x02(\rR\x05flags\"#\n\tCha\
|
ngth\x18\x01\x20\x02(\rR\ndataLength\x12\x1f\n\x0bdata_offset\x18\x02\
|
||||||
ngePin\x12\x16\n\x06remove\x18\x01\x20\x01(\x08R\x06remove\"(\n\x0eChang\
|
\x20\x02(\rR\ndataOffset\"3\n\x12TranslationDataAck\x12\x1d\n\ndata_chun\
|
||||||
eWipeCode\x12\x16\n\x06remove\x18\x01\x20\x01(\x08R\x06remove\"\xaa\x01\
|
k\x18\x01\x20\x02(\x0cR\tdataChunk\"\"\n\nApplyFlags\x12\x14\n\x05flags\
|
||||||
\n\tSdProtect\x12]\n\toperation\x18\x01\x20\x02(\x0e2?.hw.trezor.message\
|
\x18\x01\x20\x02(\rR\x05flags\"#\n\tChangePin\x12\x16\n\x06remove\x18\
|
||||||
s.management.SdProtect.SdProtectOperationTypeR\toperation\">\n\x16SdProt\
|
\x01\x20\x01(\x08R\x06remove\"(\n\x0eChangeWipeCode\x12\x16\n\x06remove\
|
||||||
ectOperationType\x12\x0b\n\x07DISABLE\x10\0\x12\n\n\x06ENABLE\x10\x01\
|
\x18\x01\x20\x01(\x08R\x06remove\"\xaa\x01\n\tSdProtect\x12]\n\toperatio\
|
||||||
\x12\x0b\n\x07REFRESH\x10\x02\"O\n\x04Ping\x12\x1a\n\x07message\x18\x01\
|
n\x18\x01\x20\x02(\x0e2?.hw.trezor.messages.management.SdProtect.SdProte\
|
||||||
\x20\x01(\t:\0R\x07message\x12+\n\x11button_protection\x18\x02\x20\x01(\
|
ctOperationTypeR\toperation\">\n\x16SdProtectOperationType\x12\x0b\n\x07\
|
||||||
\x08R\x10buttonProtection\"\x08\n\x06Cancel\"\x20\n\nGetEntropy\x12\x12\
|
DISABLE\x10\0\x12\n\n\x06ENABLE\x10\x01\x12\x0b\n\x07REFRESH\x10\x02\"O\
|
||||||
\n\x04size\x18\x01\x20\x02(\rR\x04size\"#\n\x07Entropy\x12\x18\n\x07entr\
|
\n\x04Ping\x12\x1a\n\x07message\x18\x01\x20\x01(\t:\0R\x07message\x12+\n\
|
||||||
opy\x18\x01\x20\x02(\x0cR\x07entropy\"/\n\x0fGetFirmwareHash\x12\x1c\n\t\
|
\x11button_protection\x18\x02\x20\x01(\x08R\x10buttonProtection\"\x08\n\
|
||||||
challenge\x18\x01\x20\x01(\x0cR\tchallenge\"\"\n\x0cFirmwareHash\x12\x12\
|
\x06Cancel\"\x20\n\nGetEntropy\x12\x12\n\x04size\x18\x01\x20\x02(\rR\x04\
|
||||||
\n\x04hash\x18\x01\x20\x02(\x0cR\x04hash\"2\n\x12AuthenticateDevice\x12\
|
size\"#\n\x07Entropy\x12\x18\n\x07entropy\x18\x01\x20\x02(\x0cR\x07entro\
|
||||||
\x1c\n\tchallenge\x18\x01\x20\x02(\x0cR\tchallenge\"U\n\x11AuthenticityP\
|
py\"/\n\x0fGetFirmwareHash\x12\x1c\n\tchallenge\x18\x01\x20\x01(\x0cR\tc\
|
||||||
roof\x12\"\n\x0ccertificates\x18\x01\x20\x03(\x0cR\x0ccertificates\x12\
|
hallenge\"\"\n\x0cFirmwareHash\x12\x12\n\x04hash\x18\x01\x20\x02(\x0cR\
|
||||||
\x1c\n\tsignature\x18\x02\x20\x02(\x0cR\tsignature\"\x0c\n\nWipeDevice\"\
|
\x04hash\"2\n\x12AuthenticateDevice\x12\x1c\n\tchallenge\x18\x01\x20\x02\
|
||||||
\xad\x02\n\nLoadDevice\x12\x1c\n\tmnemonics\x18\x01\x20\x03(\tR\tmnemoni\
|
(\x0cR\tchallenge\"U\n\x11AuthenticityProof\x12\"\n\x0ccertificates\x18\
|
||||||
cs\x12\x10\n\x03pin\x18\x03\x20\x01(\tR\x03pin\x123\n\x15passphrase_prot\
|
\x01\x20\x03(\x0cR\x0ccertificates\x12\x1c\n\tsignature\x18\x02\x20\x02(\
|
||||||
ection\x18\x04\x20\x01(\x08R\x14passphraseProtection\x12\x1e\n\x08langua\
|
\x0cR\tsignature\"\x0c\n\nWipeDevice\"\xad\x02\n\nLoadDevice\x12\x1c\n\t\
|
||||||
ge\x18\x05\x20\x01(\tR\x08languageB\x02\x18\x01\x12\x14\n\x05label\x18\
|
mnemonics\x18\x01\x20\x03(\tR\tmnemonics\x12\x10\n\x03pin\x18\x03\x20\
|
||||||
\x06\x20\x01(\tR\x05label\x12#\n\rskip_checksum\x18\x07\x20\x01(\x08R\
|
\x01(\tR\x03pin\x123\n\x15passphrase_protection\x18\x04\x20\x01(\x08R\
|
||||||
\x0cskipChecksum\x12\x1f\n\x0bu2f_counter\x18\x08\x20\x01(\rR\nu2fCounte\
|
\x14passphraseProtection\x12\x1e\n\x08language\x18\x05\x20\x01(\tR\x08la\
|
||||||
r\x12!\n\x0cneeds_backup\x18\t\x20\x01(\x08R\x0bneedsBackup\x12\x1b\n\tn\
|
nguageB\x02\x18\x01\x12\x14\n\x05label\x18\x06\x20\x01(\tR\x05label\x12#\
|
||||||
o_backup\x18\n\x20\x01(\x08R\x08noBackup\"\x99\x03\n\x0bResetDevice\x12%\
|
\n\rskip_checksum\x18\x07\x20\x01(\x08R\x0cskipChecksum\x12\x1f\n\x0bu2f\
|
||||||
\n\x0edisplay_random\x18\x01\x20\x01(\x08R\rdisplayRandom\x12\x1f\n\x08s\
|
_counter\x18\x08\x20\x01(\rR\nu2fCounter\x12!\n\x0cneeds_backup\x18\t\
|
||||||
trength\x18\x02\x20\x01(\r:\x03256R\x08strength\x123\n\x15passphrase_pro\
|
\x20\x01(\x08R\x0bneedsBackup\x12\x1b\n\tno_backup\x18\n\x20\x01(\x08R\
|
||||||
tection\x18\x03\x20\x01(\x08R\x14passphraseProtection\x12%\n\x0epin_prot\
|
\x08noBackup\"\x99\x03\n\x0bResetDevice\x12%\n\x0edisplay_random\x18\x01\
|
||||||
ection\x18\x04\x20\x01(\x08R\rpinProtection\x12\x1e\n\x08language\x18\
|
\x20\x01(\x08R\rdisplayRandom\x12\x1f\n\x08strength\x18\x02\x20\x01(\r:\
|
||||||
\x05\x20\x01(\tR\x08languageB\x02\x18\x01\x12\x14\n\x05label\x18\x06\x20\
|
\x03256R\x08strength\x123\n\x15passphrase_protection\x18\x03\x20\x01(\
|
||||||
\x01(\tR\x05label\x12\x1f\n\x0bu2f_counter\x18\x07\x20\x01(\rR\nu2fCount\
|
\x08R\x14passphraseProtection\x12%\n\x0epin_protection\x18\x04\x20\x01(\
|
||||||
er\x12\x1f\n\x0bskip_backup\x18\x08\x20\x01(\x08R\nskipBackup\x12\x1b\n\
|
\x08R\rpinProtection\x12\x1e\n\x08language\x18\x05\x20\x01(\tR\x08langua\
|
||||||
\tno_backup\x18\t\x20\x01(\x08R\x08noBackup\x12Q\n\x0bbackup_type\x18\n\
|
geB\x02\x18\x01\x12\x14\n\x05label\x18\x06\x20\x01(\tR\x05label\x12\x1f\
|
||||||
\x20\x01(\x0e2).hw.trezor.messages.management.BackupType:\x05Bip39R\nbac\
|
\n\x0bu2f_counter\x18\x07\x20\x01(\rR\nu2fCounter\x12\x1f\n\x0bskip_back\
|
||||||
kupType\"\xe5\x01\n\x0cBackupDevice\x12'\n\x0fgroup_threshold\x18\x01\
|
up\x18\x08\x20\x01(\x08R\nskipBackup\x12\x1b\n\tno_backup\x18\t\x20\x01(\
|
||||||
\x20\x01(\rR\x0egroupThreshold\x12O\n\x06groups\x18\x02\x20\x03(\x0b27.h\
|
\x08R\x08noBackup\x12Q\n\x0bbackup_type\x18\n\x20\x01(\x0e2).hw.trezor.m\
|
||||||
w.trezor.messages.management.BackupDevice.Slip39GroupR\x06groups\x1a[\n\
|
essages.management.BackupType:\x05Bip39R\nbackupType\"\xe5\x01\n\x0cBack\
|
||||||
\x0bSlip39Group\x12)\n\x10member_threshold\x18\x01\x20\x02(\rR\x0fmember\
|
upDevice\x12'\n\x0fgroup_threshold\x18\x01\x20\x01(\rR\x0egroupThreshold\
|
||||||
Threshold\x12!\n\x0cmember_count\x18\x02\x20\x02(\rR\x0bmemberCount\"\
|
\x12O\n\x06groups\x18\x02\x20\x03(\x0b27.hw.trezor.messages.management.B\
|
||||||
\x10\n\x0eEntropyRequest\"&\n\nEntropyAck\x12\x18\n\x07entropy\x18\x01\
|
ackupDevice.Slip39GroupR\x06groups\x1a[\n\x0bSlip39Group\x12)\n\x10membe\
|
||||||
\x20\x02(\x0cR\x07entropy\"\xd8\x03\n\x0eRecoveryDevice\x12\x1d\n\nword_\
|
r_threshold\x18\x01\x20\x02(\rR\x0fmemberThreshold\x12!\n\x0cmember_coun\
|
||||||
count\x18\x01\x20\x01(\rR\twordCount\x123\n\x15passphrase_protection\x18\
|
t\x18\x02\x20\x02(\rR\x0bmemberCount\"\x10\n\x0eEntropyRequest\"&\n\nEnt\
|
||||||
\x02\x20\x01(\x08R\x14passphraseProtection\x12%\n\x0epin_protection\x18\
|
ropyAck\x12\x18\n\x07entropy\x18\x01\x20\x02(\x0cR\x07entropy\"\xef\x04\
|
||||||
\x03\x20\x01(\x08R\rpinProtection\x12\x1e\n\x08language\x18\x04\x20\x01(\
|
\n\x0eRecoveryDevice\x12\x1d\n\nword_count\x18\x01\x20\x01(\rR\twordCoun\
|
||||||
\tR\x08languageB\x02\x18\x01\x12\x14\n\x05label\x18\x05\x20\x01(\tR\x05l\
|
t\x123\n\x15passphrase_protection\x18\x02\x20\x01(\x08R\x14passphrasePro\
|
||||||
abel\x12)\n\x10enforce_wordlist\x18\x06\x20\x01(\x08R\x0fenforceWordlist\
|
tection\x12%\n\x0epin_protection\x18\x03\x20\x01(\x08R\rpinProtection\
|
||||||
\x12T\n\x04type\x18\x08\x20\x01(\x0e2@.hw.trezor.messages.management.Rec\
|
\x12\x1e\n\x08language\x18\x04\x20\x01(\tR\x08languageB\x02\x18\x01\x12\
|
||||||
overyDevice.RecoveryDeviceTypeR\x04type\x12\x1f\n\x0bu2f_counter\x18\t\
|
\x14\n\x05label\x18\x05\x20\x01(\tR\x05label\x12)\n\x10enforce_wordlist\
|
||||||
\x20\x01(\rR\nu2fCounter\x12\x17\n\x07dry_run\x18\n\x20\x01(\x08R\x06dry\
|
\x18\x06\x20\x01(\x08R\x0fenforceWordlist\x12T\n\x04type\x18\x08\x20\x01\
|
||||||
Run\"Z\n\x12RecoveryDeviceType\x12%\n!RecoveryDeviceType_ScrambledWords\
|
(\x0e2@.hw.trezor.messages.management.RecoveryDevice.RecoveryDeviceTypeR\
|
||||||
\x10\0\x12\x1d\n\x19RecoveryDeviceType_Matrix\x10\x01\"\xc5\x01\n\x0bWor\
|
\x04type\x12\x1f\n\x0bu2f_counter\x18\t\x20\x01(\rR\nu2fCounter\x12^\n\
|
||||||
dRequest\x12N\n\x04type\x18\x01\x20\x02(\x0e2:.hw.trezor.messages.manage\
|
\x04kind\x18\n\x20\x01(\x0e2:.hw.trezor.messages.management.RecoveryDevi\
|
||||||
ment.WordRequest.WordRequestTypeR\x04type\"f\n\x0fWordRequestType\x12\
|
ce.RecoveryKind:\x0eNormalRecoveryR\x04kind\"Z\n\x12RecoveryDeviceType\
|
||||||
\x19\n\x15WordRequestType_Plain\x10\0\x12\x1b\n\x17WordRequestType_Matri\
|
\x12%\n!RecoveryDeviceType_ScrambledWords\x10\0\x12\x1d\n\x19RecoveryDev\
|
||||||
x9\x10\x01\x12\x1b\n\x17WordRequestType_Matrix6\x10\x02\"\x1d\n\x07WordA\
|
iceType_Matrix\x10\x01\"H\n\x0cRecoveryKind\x12\x12\n\x0eNormalRecovery\
|
||||||
ck\x12\x12\n\x04word\x18\x01\x20\x02(\tR\x04word\"0\n\rSetU2FCounter\x12\
|
\x10\0\x12\n\n\x06DryRun\x10\x01\x12\x18\n\x14UnlockRepeatedBackup\x10\
|
||||||
\x1f\n\x0bu2f_counter\x18\x01\x20\x02(\rR\nu2fCounter\"\x13\n\x11GetNext\
|
\x02J\x04\x08\x07\x10\x08\"\xc5\x01\n\x0bWordRequest\x12N\n\x04type\x18\
|
||||||
U2FCounter\"1\n\x0eNextU2FCounter\x12\x1f\n\x0bu2f_counter\x18\x01\x20\
|
\x01\x20\x02(\x0e2:.hw.trezor.messages.management.WordRequest.WordReques\
|
||||||
\x02(\rR\nu2fCounter\"\x11\n\x0fDoPreauthorized\"\x16\n\x14Preauthorized\
|
tTypeR\x04type\"f\n\x0fWordRequestType\x12\x19\n\x15WordRequestType_Plai\
|
||||||
Request\"\x15\n\x13CancelAuthorization\"\x9a\x02\n\x12RebootToBootloader\
|
n\x10\0\x12\x1b\n\x17WordRequestType_Matrix9\x10\x01\x12\x1b\n\x17WordRe\
|
||||||
\x12o\n\x0cboot_command\x18\x01\x20\x01(\x0e2=.hw.trezor.messages.manage\
|
questType_Matrix6\x10\x02\"\x1d\n\x07WordAck\x12\x12\n\x04word\x18\x01\
|
||||||
ment.RebootToBootloader.BootCommand:\rSTOP_AND_WAITR\x0bbootCommand\x12'\
|
\x20\x02(\tR\x04word\"0\n\rSetU2FCounter\x12\x1f\n\x0bu2f_counter\x18\
|
||||||
\n\x0ffirmware_header\x18\x02\x20\x01(\x0cR\x0efirmwareHeader\x123\n\x14\
|
\x01\x20\x02(\rR\nu2fCounter\"\x13\n\x11GetNextU2FCounter\"1\n\x0eNextU2\
|
||||||
language_data_length\x18\x03\x20\x01(\r:\x010R\x12languageDataLength\"5\
|
FCounter\x12\x1f\n\x0bu2f_counter\x18\x01\x20\x02(\rR\nu2fCounter\"\x11\
|
||||||
\n\x0bBootCommand\x12\x11\n\rSTOP_AND_WAIT\x10\0\x12\x13\n\x0fINSTALL_UP\
|
\n\x0fDoPreauthorized\"\x16\n\x14PreauthorizedRequest\"\x15\n\x13CancelA\
|
||||||
GRADE\x10\x01\"\x10\n\x08GetNonce:\x04\x88\xb2\x19\x01\"#\n\x05Nonce\x12\
|
uthorization\"\x9a\x02\n\x12RebootToBootloader\x12o\n\x0cboot_command\
|
||||||
\x14\n\x05nonce\x18\x01\x20\x02(\x0cR\x05nonce:\x04\x88\xb2\x19\x01\";\n\
|
\x18\x01\x20\x01(\x0e2=.hw.trezor.messages.management.RebootToBootloader\
|
||||||
\nUnlockPath\x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\x12\
|
.BootCommand:\rSTOP_AND_WAITR\x0bbootCommand\x12'\n\x0ffirmware_header\
|
||||||
\x10\n\x03mac\x18\x02\x20\x01(\x0cR\x03mac\"'\n\x13UnlockedPathRequest\
|
\x18\x02\x20\x01(\x0cR\x0efirmwareHeader\x123\n\x14language_data_length\
|
||||||
\x12\x10\n\x03mac\x18\x01\x20\x01(\x0cR\x03mac\"\x14\n\x12ShowDeviceTuto\
|
\x18\x03\x20\x01(\r:\x010R\x12languageDataLength\"5\n\x0bBootCommand\x12\
|
||||||
rial\"\x12\n\x10UnlockBootloader\"%\n\rSetBrightness\x12\x14\n\x05value\
|
\x11\n\rSTOP_AND_WAIT\x10\0\x12\x13\n\x0fINSTALL_UPGRADE\x10\x01\"\x10\n\
|
||||||
\x18\x01\x20\x01(\rR\x05value*\x99\x01\n\nBackupType\x12\t\n\x05Bip39\
|
\x08GetNonce:\x04\x88\xb2\x19\x01\"#\n\x05Nonce\x12\x14\n\x05nonce\x18\
|
||||||
\x10\0\x12\x10\n\x0cSlip39_Basic\x10\x01\x12\x13\n\x0fSlip39_Advanced\
|
\x01\x20\x02(\x0cR\x05nonce:\x04\x88\xb2\x19\x01\";\n\nUnlockPath\x12\
|
||||||
\x10\x02\x12\x1c\n\x18Slip39_Single_Extendable\x10\x03\x12\x1b\n\x17Slip\
|
\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\x12\x10\n\x03mac\x18\
|
||||||
39_Basic_Extendable\x10\x04\x12\x1e\n\x1aSlip39_Advanced_Extendable\x10\
|
\x02\x20\x01(\x0cR\x03mac\"'\n\x13UnlockedPathRequest\x12\x10\n\x03mac\
|
||||||
\x05*G\n\x10SafetyCheckLevel\x12\n\n\x06Strict\x10\0\x12\x10\n\x0cPrompt\
|
\x18\x01\x20\x01(\x0cR\x03mac\"\x14\n\x12ShowDeviceTutorial\"\x12\n\x10U\
|
||||||
Always\x10\x01\x12\x15\n\x11PromptTemporarily\x10\x02*0\n\x10HomescreenF\
|
nlockBootloader\"%\n\rSetBrightness\x12\x14\n\x05value\x18\x01\x20\x01(\
|
||||||
ormat\x12\x08\n\x04Toif\x10\x01\x12\x08\n\x04Jpeg\x10\x02\x12\x08\n\x04T\
|
\rR\x05value*\x99\x01\n\nBackupType\x12\t\n\x05Bip39\x10\0\x12\x10\n\x0c\
|
||||||
oiG\x10\x03BB\n#com.satoshilabs.trezor.lib.protobufB\x17TrezorMessageMan\
|
Slip39_Basic\x10\x01\x12\x13\n\x0fSlip39_Advanced\x10\x02\x12\x1c\n\x18S\
|
||||||
agement\x80\xa6\x1d\x01\
|
lip39_Single_Extendable\x10\x03\x12\x1b\n\x17Slip39_Basic_Extendable\x10\
|
||||||
|
\x04\x12\x1e\n\x1aSlip39_Advanced_Extendable\x10\x05*G\n\x10SafetyCheckL\
|
||||||
|
evel\x12\n\n\x06Strict\x10\0\x12\x10\n\x0cPromptAlways\x10\x01\x12\x15\n\
|
||||||
|
\x11PromptTemporarily\x10\x02*0\n\x10HomescreenFormat\x12\x08\n\x04Toif\
|
||||||
|
\x10\x01\x12\x08\n\x04Jpeg\x10\x02\x12\x08\n\x04ToiG\x10\x03BB\n#com.sat\
|
||||||
|
oshilabs.trezor.lib.protobufB\x17TrezorMessageManagement\x80\xa6\x1d\x01\
|
||||||
";
|
";
|
||||||
|
|
||||||
/// `FileDescriptorProto` object which was a source for this generated file
|
/// `FileDescriptorProto` object which was a source for this generated file
|
||||||
@ -11064,13 +11215,15 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor {
|
|||||||
messages.push(UnlockBootloader::generated_message_descriptor_data());
|
messages.push(UnlockBootloader::generated_message_descriptor_data());
|
||||||
messages.push(SetBrightness::generated_message_descriptor_data());
|
messages.push(SetBrightness::generated_message_descriptor_data());
|
||||||
messages.push(backup_device::Slip39Group::generated_message_descriptor_data());
|
messages.push(backup_device::Slip39Group::generated_message_descriptor_data());
|
||||||
let mut enums = ::std::vec::Vec::with_capacity(8);
|
let mut enums = ::std::vec::Vec::with_capacity(10);
|
||||||
enums.push(BackupType::generated_enum_descriptor_data());
|
enums.push(BackupType::generated_enum_descriptor_data());
|
||||||
enums.push(SafetyCheckLevel::generated_enum_descriptor_data());
|
enums.push(SafetyCheckLevel::generated_enum_descriptor_data());
|
||||||
enums.push(HomescreenFormat::generated_enum_descriptor_data());
|
enums.push(HomescreenFormat::generated_enum_descriptor_data());
|
||||||
|
enums.push(features::RecoveryStatus::generated_enum_descriptor_data());
|
||||||
enums.push(features::Capability::generated_enum_descriptor_data());
|
enums.push(features::Capability::generated_enum_descriptor_data());
|
||||||
enums.push(sd_protect::SdProtectOperationType::generated_enum_descriptor_data());
|
enums.push(sd_protect::SdProtectOperationType::generated_enum_descriptor_data());
|
||||||
enums.push(recovery_device::RecoveryDeviceType::generated_enum_descriptor_data());
|
enums.push(recovery_device::RecoveryDeviceType::generated_enum_descriptor_data());
|
||||||
|
enums.push(recovery_device::RecoveryKind::generated_enum_descriptor_data());
|
||||||
enums.push(word_request::WordRequestType::generated_enum_descriptor_data());
|
enums.push(word_request::WordRequestType::generated_enum_descriptor_data());
|
||||||
enums.push(reboot_to_bootloader::BootCommand::generated_enum_descriptor_data());
|
enums.push(reboot_to_bootloader::BootCommand::generated_enum_descriptor_data());
|
||||||
::protobuf::reflect::GeneratedFileDescriptor::new_generated(
|
::protobuf::reflect::GeneratedFileDescriptor::new_generated(
|
||||||
|
@ -48,25 +48,27 @@ def enter_word(
|
|||||||
raise ValueError("Unknown model")
|
raise ValueError("Unknown model")
|
||||||
|
|
||||||
|
|
||||||
def confirm_recovery(debug: "DebugLink") -> None:
|
def confirm_recovery(debug: "DebugLink", title: str = "recovery__title") -> None:
|
||||||
layout = debug.wait_layout()
|
layout = debug.wait_layout()
|
||||||
TR.assert_equals(layout.title(), "recovery__title")
|
TR.assert_equals(layout.title(), title)
|
||||||
if debug.model in (models.T2T1,):
|
if debug.model in (models.T2T1,):
|
||||||
debug.click(buttons.OK, wait=True)
|
debug.click(buttons.OK, wait=True)
|
||||||
elif debug.model in (models.T3T1,):
|
elif debug.model in (models.T3T1,):
|
||||||
debug.swipe_up(wait=True)
|
debug.swipe_up(wait=True)
|
||||||
elif debug.model in (models.T2B1,):
|
elif debug.model in (models.T2B1,):
|
||||||
debug.press_right(wait=True)
|
debug.press_right(wait=True)
|
||||||
debug.press_right()
|
|
||||||
|
|
||||||
|
|
||||||
def select_number_of_words(
|
def select_number_of_words(
|
||||||
debug: "DebugLink", num_of_words: int = 20, wait: bool = True
|
debug: "DebugLink",
|
||||||
|
num_of_words: int = 20,
|
||||||
|
wait: bool = True,
|
||||||
|
unlock_repeated_backup=False,
|
||||||
) -> None:
|
) -> None:
|
||||||
if wait:
|
if wait:
|
||||||
debug.wait_layout()
|
debug.wait_layout()
|
||||||
TR.assert_equals(debug.read_layout().text_content(), "recovery__num_of_words")
|
|
||||||
if debug.model in (models.T2T1,):
|
if debug.model in (models.T2T1,):
|
||||||
|
TR.assert_equals(debug.read_layout().text_content(), "recovery__num_of_words")
|
||||||
# click the number
|
# click the number
|
||||||
word_option_offset = 6
|
word_option_offset = 6
|
||||||
word_options = (12, 18, 20, 24, 33)
|
word_options = (12, 18, 20, 24, 33)
|
||||||
@ -102,12 +104,14 @@ def select_number_of_words(
|
|||||||
else:
|
else:
|
||||||
raise ValueError("Unknown model")
|
raise ValueError("Unknown model")
|
||||||
|
|
||||||
if num_of_words in (20, 33):
|
if unlock_repeated_backup:
|
||||||
|
TR.assert_in(layout.text_content(), "recovery__enter_backup")
|
||||||
|
elif num_of_words in (20, 33):
|
||||||
TR.assert_in_multiple(
|
TR.assert_in_multiple(
|
||||||
layout.text_content(),
|
layout.text_content(),
|
||||||
["recovery__enter_any_share", "recovery__only_first_n_letters"],
|
["recovery__enter_any_share", "recovery__only_first_n_letters"],
|
||||||
)
|
)
|
||||||
else:
|
else: # BIP-39
|
||||||
TR.assert_in_multiple(
|
TR.assert_in_multiple(
|
||||||
layout.text_content(),
|
layout.text_content(),
|
||||||
["recovery__enter_backup", "recovery__only_first_n_letters"],
|
["recovery__enter_backup", "recovery__only_first_n_letters"],
|
||||||
@ -115,7 +119,10 @@ def select_number_of_words(
|
|||||||
|
|
||||||
|
|
||||||
def enter_share(
|
def enter_share(
|
||||||
debug: "DebugLink", share: str, is_first: bool = True
|
debug: "DebugLink",
|
||||||
|
share: str,
|
||||||
|
is_first: bool = True,
|
||||||
|
before_title: str = "recovery__title_recover",
|
||||||
) -> "LayoutContent":
|
) -> "LayoutContent":
|
||||||
if debug.model in (models.T2B1,):
|
if debug.model in (models.T2B1,):
|
||||||
TR.assert_in(debug.read_layout().title(), "recovery__title_recover")
|
TR.assert_in(debug.read_layout().title(), "recovery__title_recover")
|
||||||
@ -136,13 +143,21 @@ def enter_share(
|
|||||||
return layout
|
return layout
|
||||||
|
|
||||||
|
|
||||||
def enter_shares(debug: "DebugLink", shares: list[str]) -> None:
|
def enter_shares(
|
||||||
|
debug: "DebugLink",
|
||||||
|
shares: list[str],
|
||||||
|
enter_share_before_title: str = "recovery__title_recover",
|
||||||
|
text: str = "recovery__enter_any_share",
|
||||||
|
after_layout_text: str = "recovery__wallet_recovered",
|
||||||
|
) -> None:
|
||||||
TR.assert_in_multiple(
|
TR.assert_in_multiple(
|
||||||
debug.read_layout().text_content(),
|
debug.read_layout().text_content(),
|
||||||
["recovery__enter_any_share", "recovery__only_first_n_letters"],
|
["recovery__enter_any_share", "recovery__only_first_n_letters", text],
|
||||||
)
|
)
|
||||||
for index, share in enumerate(shares):
|
for index, share in enumerate(shares):
|
||||||
enter_share(debug, share, is_first=index == 0)
|
enter_share(
|
||||||
|
debug, share, is_first=index == 0, before_title=enter_share_before_title
|
||||||
|
)
|
||||||
if index < len(shares) - 1:
|
if index < len(shares) - 1:
|
||||||
# FIXME: when ui-t3t1 done for shamir, we want to check the template below
|
# FIXME: when ui-t3t1 done for shamir, we want to check the template below
|
||||||
TR.assert_in(debug.read_layout().title(), "recovery__title_recover")
|
TR.assert_in(debug.read_layout().title(), "recovery__title_recover")
|
||||||
@ -152,16 +167,22 @@ def enter_shares(debug: "DebugLink", shares: list[str]) -> None:
|
|||||||
# template=(index + 1, len(shares)),
|
# template=(index + 1, len(shares)),
|
||||||
# )
|
# )
|
||||||
|
|
||||||
TR.assert_in(debug.read_layout().text_content(), "recovery__wallet_recovered")
|
TR.assert_in(debug.read_layout().text_content(), after_layout_text)
|
||||||
|
|
||||||
|
|
||||||
def enter_seed(debug: "DebugLink", seed_words: list[str]) -> None:
|
def enter_seed(
|
||||||
prepare_enter_seed(debug)
|
debug: "DebugLink",
|
||||||
|
seed_words: list[str],
|
||||||
|
is_slip39=False,
|
||||||
|
prepare_layout_text: str = "recovery__enter_backup",
|
||||||
|
after_layout_text: str = "recovery__wallet_recovered",
|
||||||
|
) -> None:
|
||||||
|
prepare_enter_seed(debug, prepare_layout_text)
|
||||||
|
|
||||||
for word in seed_words:
|
for word in seed_words:
|
||||||
enter_word(debug, word, is_slip39=False)
|
enter_word(debug, word, is_slip39=is_slip39)
|
||||||
|
|
||||||
TR.assert_in(debug.read_layout().text_content(), "recovery__wallet_recovered")
|
TR.assert_in(debug.read_layout().text_content(), after_layout_text)
|
||||||
|
|
||||||
|
|
||||||
def enter_seed_previous_correct(
|
def enter_seed_previous_correct(
|
||||||
@ -209,10 +230,12 @@ def enter_seed_previous_correct(
|
|||||||
# TR.assert_in(debug.read_layout().text_content(), "recovery__wallet_recovered")
|
# TR.assert_in(debug.read_layout().text_content(), "recovery__wallet_recovered")
|
||||||
|
|
||||||
|
|
||||||
def prepare_enter_seed(debug: "DebugLink") -> None:
|
def prepare_enter_seed(
|
||||||
|
debug: "DebugLink", layout_text: str = "recovery__enter_backup"
|
||||||
|
) -> None:
|
||||||
TR.assert_in_multiple(
|
TR.assert_in_multiple(
|
||||||
debug.read_layout().text_content(),
|
debug.read_layout().text_content(),
|
||||||
["recovery__enter_backup", "recovery__only_first_n_letters"],
|
["recovery__enter_backup", "recovery__only_first_n_letters", layout_text],
|
||||||
)
|
)
|
||||||
if debug.model in (models.T2T1,):
|
if debug.model in (models.T2T1,):
|
||||||
debug.click(buttons.OK, wait=True)
|
debug.click(buttons.OK, wait=True)
|
||||||
@ -221,7 +244,6 @@ def prepare_enter_seed(debug: "DebugLink") -> None:
|
|||||||
debug.swipe_up(wait=True)
|
debug.swipe_up(wait=True)
|
||||||
elif debug.model in (models.T2B1,):
|
elif debug.model in (models.T2B1,):
|
||||||
debug.press_right(wait=True)
|
debug.press_right(wait=True)
|
||||||
TR.assert_equals(debug.read_layout().title(), "recovery__title_recover")
|
|
||||||
debug.press_right()
|
debug.press_right()
|
||||||
layout = debug.press_right(wait=True)
|
layout = debug.press_right(wait=True)
|
||||||
assert "MnemonicKeyboard" in layout.all_components()
|
assert "MnemonicKeyboard" in layout.all_components()
|
||||||
|
@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
|
|||||||
|
|
||||||
from shamir_mnemonic import shamir # type: ignore
|
from shamir_mnemonic import shamir # type: ignore
|
||||||
|
|
||||||
from trezorlib import messages, models
|
from trezorlib import models
|
||||||
|
|
||||||
from .. import buttons
|
from .. import buttons
|
||||||
from .. import translations as TR
|
from .. import translations as TR
|
||||||
@ -81,9 +81,7 @@ def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> Non
|
|||||||
debug.press_middle(wait=True)
|
debug.press_middle(wait=True)
|
||||||
|
|
||||||
|
|
||||||
def read_words(
|
def read_words(debug: "DebugLink", do_htc: bool = True) -> list[str]:
|
||||||
debug: "DebugLink", backup_type: messages.BackupType, do_htc: bool = True
|
|
||||||
) -> list[str]:
|
|
||||||
words: list[str] = []
|
words: list[str] = []
|
||||||
|
|
||||||
if debug.model in (models.T2B1,):
|
if debug.model in (models.T2B1,):
|
||||||
|
@ -82,7 +82,7 @@ def test_backup_slip39_custom(
|
|||||||
all_words: list[str] = []
|
all_words: list[str] = []
|
||||||
for _ in range(share_count):
|
for _ in range(share_count):
|
||||||
# read words
|
# read words
|
||||||
words = reset.read_words(debug, messages.BackupType.Slip39_Basic)
|
words = reset.read_words(debug)
|
||||||
|
|
||||||
# confirm words
|
# confirm words
|
||||||
reset.confirm_words(debug, words)
|
reset.confirm_words(debug, words)
|
||||||
|
@ -47,7 +47,7 @@ def prepare_recovery_and_evaluate(
|
|||||||
assert isinstance(device_handler.result(), messages.Success)
|
assert isinstance(device_handler.result(), messages.Success)
|
||||||
features = device_handler.features()
|
features = device_handler.features()
|
||||||
assert features.initialized is True
|
assert features.initialized is True
|
||||||
assert features.recovery_mode is False
|
assert features.recovery_status == messages.RecoveryStatus.NoRecovery
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.setup_client(uninitialized=True)
|
@pytest.mark.setup_client(uninitialized=True)
|
||||||
|
218
tests/click_tests/test_repeated_backup.py
Normal file
218
tests/click_tests/test_repeated_backup.py
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
# This file is part of the Trezor project.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2012-2024 SatoshiLabs and contributors
|
||||||
|
#
|
||||||
|
# This library is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License version 3
|
||||||
|
# as published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the License along with this library.
|
||||||
|
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from trezorlib import device, messages
|
||||||
|
|
||||||
|
from .. import buttons
|
||||||
|
from ..common import WITH_MOCK_URANDOM
|
||||||
|
from . import recovery, reset
|
||||||
|
from .common import go_next
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from ..device_handler import BackgroundDeviceHandler
|
||||||
|
|
||||||
|
|
||||||
|
pytestmark = [pytest.mark.skip_t1b1]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.setup_client(uninitialized=True)
|
||||||
|
@WITH_MOCK_URANDOM
|
||||||
|
def test_repeated_backup(
|
||||||
|
device_handler: "BackgroundDeviceHandler",
|
||||||
|
):
|
||||||
|
features = device_handler.features()
|
||||||
|
debug = device_handler.debuglink()
|
||||||
|
|
||||||
|
assert features.initialized is False
|
||||||
|
|
||||||
|
device_handler.run(
|
||||||
|
device.reset,
|
||||||
|
strength=128,
|
||||||
|
backup_type=messages.BackupType.Slip39_Basic,
|
||||||
|
pin_protection=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# confirm new wallet
|
||||||
|
reset.confirm_new_wallet(debug)
|
||||||
|
|
||||||
|
# confirm back up
|
||||||
|
reset.confirm_read(debug)
|
||||||
|
|
||||||
|
# confirm backup warning
|
||||||
|
reset.confirm_read(debug, middle_r=True)
|
||||||
|
|
||||||
|
# let's make a 1-of-1 backup to start with...
|
||||||
|
|
||||||
|
# shares=1
|
||||||
|
reset.set_selection(debug, buttons.RESET_MINUS, 5 - 1)
|
||||||
|
|
||||||
|
# confirm checklist
|
||||||
|
reset.confirm_read(debug)
|
||||||
|
|
||||||
|
# threshold=1
|
||||||
|
reset.set_selection(debug, buttons.RESET_PLUS, 0)
|
||||||
|
|
||||||
|
# confirm checklist
|
||||||
|
reset.confirm_read(debug)
|
||||||
|
|
||||||
|
# confirm backup warning
|
||||||
|
reset.confirm_read(debug, middle_r=True)
|
||||||
|
|
||||||
|
# read words
|
||||||
|
initial_backup_1_of_1 = reset.read_words(debug)
|
||||||
|
|
||||||
|
# confirm words
|
||||||
|
reset.confirm_words(debug, initial_backup_1_of_1)
|
||||||
|
|
||||||
|
# confirm share checked
|
||||||
|
reset.confirm_read(debug)
|
||||||
|
|
||||||
|
# confirm backup done
|
||||||
|
reset.confirm_read(debug)
|
||||||
|
|
||||||
|
# Your backup is done
|
||||||
|
go_next(debug)
|
||||||
|
|
||||||
|
# great ... device is initialized, backup done, and we are not in recovery mode!
|
||||||
|
assert device_handler.result() == "Initialized"
|
||||||
|
features = device_handler.features()
|
||||||
|
assert features.backup_type is messages.BackupType.Slip39_Basic
|
||||||
|
assert features.initialized is True
|
||||||
|
assert features.needs_backup is False
|
||||||
|
assert features.no_backup is False
|
||||||
|
assert features.recovery_status == messages.RecoveryStatus.NoRecovery
|
||||||
|
|
||||||
|
# run recovery to unlock backup
|
||||||
|
device_handler.run(
|
||||||
|
device.recover,
|
||||||
|
recovery_kind=messages.RecoveryKind.UnlockRepeatedBackup,
|
||||||
|
)
|
||||||
|
|
||||||
|
recovery.confirm_recovery(debug, "recovery__title_unlock_repeated_backup")
|
||||||
|
|
||||||
|
recovery.select_number_of_words(debug, num_of_words=20, unlock_repeated_backup=True)
|
||||||
|
recovery.enter_seed(
|
||||||
|
debug,
|
||||||
|
initial_backup_1_of_1,
|
||||||
|
True,
|
||||||
|
"recovery__enter_backup",
|
||||||
|
"recovery__unlock_repeated_backup",
|
||||||
|
)
|
||||||
|
|
||||||
|
# backup is enabled
|
||||||
|
go_next(debug)
|
||||||
|
|
||||||
|
assert device_handler.result().message == "Backup unlocked"
|
||||||
|
|
||||||
|
# we are now in recovery mode
|
||||||
|
features = device_handler.features()
|
||||||
|
assert features.backup_type is messages.BackupType.Slip39_Basic
|
||||||
|
assert features.initialized is True
|
||||||
|
assert features.needs_backup is False
|
||||||
|
assert features.no_backup is False
|
||||||
|
assert (
|
||||||
|
features.recovery_status
|
||||||
|
== messages.RecoveryStatus.InUnlockRepeatedBackupRecovery
|
||||||
|
)
|
||||||
|
|
||||||
|
# at this point, the backup is unlocked...
|
||||||
|
|
||||||
|
# ... so let's try to do a 2-of-3 backup
|
||||||
|
|
||||||
|
# confirm checklist
|
||||||
|
reset.confirm_read(debug)
|
||||||
|
|
||||||
|
# shares=3
|
||||||
|
reset.set_selection(debug, buttons.RESET_MINUS, 5 - 3)
|
||||||
|
|
||||||
|
# confirm checklist
|
||||||
|
reset.confirm_read(debug)
|
||||||
|
|
||||||
|
# threshold=2
|
||||||
|
reset.set_selection(debug, buttons.RESET_MINUS, 1)
|
||||||
|
|
||||||
|
# confirm checklist
|
||||||
|
reset.confirm_read(debug)
|
||||||
|
|
||||||
|
# confirm backup warning
|
||||||
|
reset.confirm_read(debug, middle_r=True)
|
||||||
|
|
||||||
|
second_backup_2_of_3: list[str] = []
|
||||||
|
for _ in range(3):
|
||||||
|
# read words
|
||||||
|
words = reset.read_words(debug, do_htc=False)
|
||||||
|
|
||||||
|
# confirm words
|
||||||
|
reset.confirm_words(debug, words)
|
||||||
|
|
||||||
|
# confirm share checked
|
||||||
|
reset.confirm_read(debug)
|
||||||
|
|
||||||
|
second_backup_2_of_3.append(" ".join(words))
|
||||||
|
|
||||||
|
# we are not in recovery mode anymore, because we finished the backup process!
|
||||||
|
features = device_handler.features()
|
||||||
|
assert features.backup_type is messages.BackupType.Slip39_Basic
|
||||||
|
assert features.initialized is True
|
||||||
|
assert features.needs_backup is False
|
||||||
|
assert features.no_backup is False
|
||||||
|
assert features.recovery_status == messages.RecoveryStatus.NoRecovery
|
||||||
|
|
||||||
|
# try to unlock backup again...
|
||||||
|
device_handler.run(
|
||||||
|
device.recover,
|
||||||
|
recovery_kind=messages.RecoveryKind.UnlockRepeatedBackup,
|
||||||
|
)
|
||||||
|
|
||||||
|
recovery.confirm_recovery(debug, "recovery__title_unlock_repeated_backup")
|
||||||
|
|
||||||
|
# ... this time with the 2 shares from the *new* backup, which was a 2-of-3!
|
||||||
|
recovery.select_number_of_words(debug, num_of_words=20, unlock_repeated_backup=True)
|
||||||
|
recovery.enter_shares(
|
||||||
|
debug,
|
||||||
|
second_backup_2_of_3[-2:],
|
||||||
|
"recovery__title_unlock_repeated_backup",
|
||||||
|
"recovery__enter_backup",
|
||||||
|
"recovery__unlock_repeated_backup",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert device_handler.result().message == "Backup unlocked"
|
||||||
|
|
||||||
|
# we are now in recovery mode again!
|
||||||
|
features = device_handler.features()
|
||||||
|
assert features.backup_type is messages.BackupType.Slip39_Basic
|
||||||
|
assert features.initialized is True
|
||||||
|
assert features.needs_backup is False
|
||||||
|
assert features.no_backup is False
|
||||||
|
assert (
|
||||||
|
features.recovery_status
|
||||||
|
== messages.RecoveryStatus.InUnlockRepeatedBackupRecovery
|
||||||
|
)
|
||||||
|
|
||||||
|
# but if we cancel the backup at this point...
|
||||||
|
reset.cancel_backup(debug)
|
||||||
|
|
||||||
|
# ...we are out of recovery mode!
|
||||||
|
features = device_handler.features()
|
||||||
|
assert features.backup_type is messages.BackupType.Slip39_Basic
|
||||||
|
assert features.initialized is True
|
||||||
|
assert features.needs_backup is False
|
||||||
|
assert features.no_backup is False
|
||||||
|
assert features.recovery_status == messages.RecoveryStatus.NoRecovery
|
@ -56,7 +56,7 @@ def test_reset_bip39(device_handler: "BackgroundDeviceHandler"):
|
|||||||
reset.confirm_read(debug, middle_r=True)
|
reset.confirm_read(debug, middle_r=True)
|
||||||
|
|
||||||
# read words
|
# read words
|
||||||
words = reset.read_words(debug, messages.BackupType.Bip39)
|
words = reset.read_words(debug)
|
||||||
|
|
||||||
# confirm words
|
# confirm words
|
||||||
reset.confirm_words(debug, words)
|
reset.confirm_words(debug, words)
|
||||||
|
@ -114,9 +114,7 @@ def test_reset_slip39_advanced(
|
|||||||
for _ in range(group_count):
|
for _ in range(group_count):
|
||||||
for _ in range(share_count):
|
for _ in range(share_count):
|
||||||
# read words
|
# read words
|
||||||
words = reset.read_words(
|
words = reset.read_words(debug, do_htc=False)
|
||||||
debug, messages.BackupType.Slip39_Advanced, do_htc=False
|
|
||||||
)
|
|
||||||
|
|
||||||
# confirm words
|
# confirm words
|
||||||
reset.confirm_words(debug, words)
|
reset.confirm_words(debug, words)
|
||||||
|
@ -93,7 +93,7 @@ def test_reset_slip39_basic(
|
|||||||
all_words: list[str] = []
|
all_words: list[str] = []
|
||||||
for _ in range(num_of_shares):
|
for _ in range(num_of_shares):
|
||||||
# read words
|
# read words
|
||||||
words = reset.read_words(debug, messages.BackupType.Slip39_Basic)
|
words = reset.read_words(debug)
|
||||||
|
|
||||||
# confirm words
|
# confirm words
|
||||||
reset.confirm_words(debug, words)
|
reset.confirm_words(debug, words)
|
||||||
|
@ -40,7 +40,7 @@ def do_recover_legacy(client: Client, mnemonic: list[str], **kwargs: Any):
|
|||||||
|
|
||||||
ret = device.recover(
|
ret = device.recover(
|
||||||
client,
|
client,
|
||||||
dry_run=True,
|
recovery_kind=messages.RecoveryKind.DryRun,
|
||||||
word_count=len(mnemonic),
|
word_count=len(mnemonic),
|
||||||
type=messages.RecoveryDeviceType.ScrambledWords,
|
type=messages.RecoveryDeviceType.ScrambledWords,
|
||||||
input_callback=input_callback,
|
input_callback=input_callback,
|
||||||
@ -56,7 +56,7 @@ def do_recover_core(client: Client, mnemonic: list[str], mismatch: bool = False)
|
|||||||
client.watch_layout()
|
client.watch_layout()
|
||||||
IF = InputFlowBip39RecoveryDryRun(client, mnemonic, mismatch=mismatch)
|
IF = InputFlowBip39RecoveryDryRun(client, mnemonic, mismatch=mismatch)
|
||||||
client.set_input_flow(IF.get())
|
client.set_input_flow(IF.get())
|
||||||
return device.recover(client, dry_run=True)
|
return device.recover(client, recovery_kind=messages.RecoveryKind.DryRun)
|
||||||
|
|
||||||
|
|
||||||
def do_recover(client: Client, mnemonic: list[str], mismatch: bool = False):
|
def do_recover(client: Client, mnemonic: list[str], mismatch: bool = False):
|
||||||
@ -105,7 +105,7 @@ def test_uninitialized(client: Client):
|
|||||||
|
|
||||||
|
|
||||||
DRY_RUN_ALLOWED_FIELDS = (
|
DRY_RUN_ALLOWED_FIELDS = (
|
||||||
"dry_run",
|
"kind",
|
||||||
"word_count",
|
"word_count",
|
||||||
"enforce_wordlist",
|
"enforce_wordlist",
|
||||||
"type",
|
"type",
|
||||||
@ -131,6 +131,8 @@ def _make_bad_params():
|
|||||||
yield field.name, True
|
yield field.name, True
|
||||||
elif field.type == "string":
|
elif field.type == "string":
|
||||||
yield field.name, "test"
|
yield field.name, "test"
|
||||||
|
elif field.type == "RecoveryKind":
|
||||||
|
yield field.name, 1
|
||||||
else:
|
else:
|
||||||
# Someone added a field to RecoveryDevice of a type that has no assigned
|
# Someone added a field to RecoveryDevice of a type that has no assigned
|
||||||
# default value. This test must be fixed.
|
# default value. This test must be fixed.
|
||||||
@ -140,13 +142,14 @@ def _make_bad_params():
|
|||||||
@pytest.mark.parametrize("field_name, field_value", _make_bad_params())
|
@pytest.mark.parametrize("field_name, field_value", _make_bad_params())
|
||||||
def test_bad_parameters(client: Client, field_name: str, field_value: Any):
|
def test_bad_parameters(client: Client, field_name: str, field_value: Any):
|
||||||
msg = messages.RecoveryDevice(
|
msg = messages.RecoveryDevice(
|
||||||
dry_run=True,
|
kind=messages.RecoveryKind.DryRun,
|
||||||
word_count=12,
|
word_count=12,
|
||||||
enforce_wordlist=True,
|
enforce_wordlist=True,
|
||||||
type=messages.RecoveryDeviceType.ScrambledWords,
|
type=messages.RecoveryDeviceType.ScrambledWords,
|
||||||
)
|
)
|
||||||
setattr(msg, field_name, field_value)
|
setattr(msg, field_name, field_value)
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
exceptions.TrezorFailure, match="Forbidden field set in dry-run"
|
exceptions.TrezorFailure,
|
||||||
|
match="Forbidden field set in dry-run",
|
||||||
):
|
):
|
||||||
client.call(msg)
|
client.call(msg)
|
||||||
|
@ -202,10 +202,10 @@ def test_already_initialized(client: Client):
|
|||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError):
|
||||||
device.recover(
|
device.recover(
|
||||||
client,
|
client,
|
||||||
12,
|
word_count=12,
|
||||||
False,
|
pin_protection=False,
|
||||||
False,
|
passphrase_protection=False,
|
||||||
"label",
|
label="label",
|
||||||
input_callback=client.mnemonic_callback,
|
input_callback=client.mnemonic_callback,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
110
tests/device_tests/test_repeated_backup.py
Normal file
110
tests/device_tests/test_repeated_backup.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
# This file is part of the Trezor project.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2012-2024 SatoshiLabs and contributors
|
||||||
|
#
|
||||||
|
# This library is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License version 3
|
||||||
|
# as published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the License along with this library.
|
||||||
|
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
|
||||||
|
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from trezorlib import device, messages
|
||||||
|
from trezorlib.debuglink import TrezorClientDebugLink as Client
|
||||||
|
from trezorlib.exceptions import Cancelled, TrezorFailure
|
||||||
|
|
||||||
|
from ..common import WITH_MOCK_URANDOM, MNEMONIC_SLIP39_BASIC_20_3of6
|
||||||
|
from ..input_flows import InputFlowSlip39BasicBackup, InputFlowSlip39BasicRecoveryDryRun
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.setup_client(needs_backup=True, mnemonic=MNEMONIC_SLIP39_BASIC_20_3of6)
|
||||||
|
@pytest.mark.skip_t1b1
|
||||||
|
@WITH_MOCK_URANDOM
|
||||||
|
def test_repeated_backup(client: Client):
|
||||||
|
assert client.features.needs_backup is True
|
||||||
|
|
||||||
|
# initial device backup
|
||||||
|
mnemonics = []
|
||||||
|
with client:
|
||||||
|
IF = InputFlowSlip39BasicBackup(client, False)
|
||||||
|
client.set_input_flow(IF.get())
|
||||||
|
device.backup(client)
|
||||||
|
mnemonics = IF.mnemonics
|
||||||
|
|
||||||
|
assert len(mnemonics) == 5
|
||||||
|
|
||||||
|
# cannot backup, since we already just did that!
|
||||||
|
with pytest.raises(TrezorFailure, match=r".*Seed already backed up"):
|
||||||
|
device.backup(client)
|
||||||
|
|
||||||
|
# unlock repeated backup by entering 3 of the 5 shares we have got
|
||||||
|
with client:
|
||||||
|
IF = InputFlowSlip39BasicRecoveryDryRun(
|
||||||
|
client, mnemonics[:3], unlock_repeated_backup=True
|
||||||
|
)
|
||||||
|
client.set_input_flow(IF.get())
|
||||||
|
ret = device.recover(
|
||||||
|
client, recovery_kind=messages.RecoveryKind.UnlockRepeatedBackup
|
||||||
|
)
|
||||||
|
assert ret == messages.Success(message="Backup unlocked")
|
||||||
|
|
||||||
|
# we can now perform another backup
|
||||||
|
with client:
|
||||||
|
IF = InputFlowSlip39BasicBackup(client, False)
|
||||||
|
client.set_input_flow(IF.get())
|
||||||
|
device.backup(client)
|
||||||
|
|
||||||
|
# the backup feature is locked again...
|
||||||
|
with pytest.raises(TrezorFailure, match=r".*Seed already backed up"):
|
||||||
|
device.backup(client)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.setup_client(needs_backup=True, mnemonic=MNEMONIC_SLIP39_BASIC_20_3of6)
|
||||||
|
@pytest.mark.skip_t1b1
|
||||||
|
@WITH_MOCK_URANDOM
|
||||||
|
def test_repeated_backup_cancel(client: Client):
|
||||||
|
assert client.features.needs_backup is True
|
||||||
|
|
||||||
|
# initial device backup
|
||||||
|
mnemonics = []
|
||||||
|
with client:
|
||||||
|
IF = InputFlowSlip39BasicBackup(client, False)
|
||||||
|
client.set_input_flow(IF.get())
|
||||||
|
device.backup(client)
|
||||||
|
mnemonics = IF.mnemonics
|
||||||
|
|
||||||
|
assert len(mnemonics) == 5
|
||||||
|
|
||||||
|
# cannot backup, since we already just did that!
|
||||||
|
with pytest.raises(TrezorFailure, match=r".*Seed already backed up"):
|
||||||
|
device.backup(client)
|
||||||
|
|
||||||
|
# unlock repeated backup by entering 3 of the 5 shares we have got
|
||||||
|
with client:
|
||||||
|
IF = InputFlowSlip39BasicRecoveryDryRun(
|
||||||
|
client, mnemonics[:3], unlock_repeated_backup=True
|
||||||
|
)
|
||||||
|
client.set_input_flow(IF.get())
|
||||||
|
ret = device.recover(
|
||||||
|
client, recovery_kind=messages.RecoveryKind.UnlockRepeatedBackup
|
||||||
|
)
|
||||||
|
assert ret == messages.Success(message="Backup unlocked")
|
||||||
|
|
||||||
|
client.debug.wait_layout()
|
||||||
|
|
||||||
|
# send a Cancel message
|
||||||
|
|
||||||
|
with pytest.raises(Cancelled):
|
||||||
|
client.call(messages.Cancel())
|
||||||
|
|
||||||
|
# the backup feature is locked again...
|
||||||
|
with pytest.raises(TrezorFailure, match=r".*Seed already backed up"):
|
||||||
|
device.backup(client)
|
@ -1979,15 +1979,25 @@ class InputFlowSlip39AdvancedRecoveryShareAlreadyEntered(InputFlowBase):
|
|||||||
|
|
||||||
|
|
||||||
class InputFlowSlip39BasicRecoveryDryRun(InputFlowBase):
|
class InputFlowSlip39BasicRecoveryDryRun(InputFlowBase):
|
||||||
def __init__(self, client: Client, shares: list[str], mismatch: bool = False):
|
def __init__(
|
||||||
|
self,
|
||||||
|
client: Client,
|
||||||
|
shares: list[str],
|
||||||
|
mismatch: bool = False,
|
||||||
|
unlock_repeated_backup=False,
|
||||||
|
):
|
||||||
super().__init__(client)
|
super().__init__(client)
|
||||||
self.shares = shares
|
self.shares = shares
|
||||||
self.mismatch = mismatch
|
self.mismatch = mismatch
|
||||||
|
self.unlock_repeated_backup = unlock_repeated_backup
|
||||||
self.word_count = len(shares[0].split(" "))
|
self.word_count = len(shares[0].split(" "))
|
||||||
|
|
||||||
def input_flow_common(self) -> BRGeneratorType:
|
def input_flow_common(self) -> BRGeneratorType:
|
||||||
yield from self.REC.confirm_dry_run()
|
yield from self.REC.confirm_dry_run()
|
||||||
yield from self.REC.setup_slip39_recovery(self.word_count)
|
if self.unlock_repeated_backup:
|
||||||
|
yield from self.REC.setup_repeated_backup_recovery(self.word_count)
|
||||||
|
else:
|
||||||
|
yield from self.REC.setup_slip39_recovery(self.word_count)
|
||||||
yield from self.REC.input_all_slip39_shares(self.shares)
|
yield from self.REC.input_all_slip39_shares(self.shares)
|
||||||
if self.mismatch:
|
if self.mismatch:
|
||||||
yield from self.REC.warning_slip39_dryrun_mismatch()
|
yield from self.REC.warning_slip39_dryrun_mismatch()
|
||||||
|
@ -73,6 +73,12 @@ class RecoveryFlow:
|
|||||||
yield from self.input_number_of_words(num_words)
|
yield from self.input_number_of_words(num_words)
|
||||||
yield from self.enter_any_share()
|
yield from self.enter_any_share()
|
||||||
|
|
||||||
|
def setup_repeated_backup_recovery(self, num_words: int) -> BRGeneratorType:
|
||||||
|
if self.client.model is models.T2B1:
|
||||||
|
yield from self.tr_recovery_homescreen()
|
||||||
|
yield from self.input_number_of_words(num_words)
|
||||||
|
yield from self.enter_your_backup()
|
||||||
|
|
||||||
def setup_bip39_recovery(self, num_words: int) -> BRGeneratorType:
|
def setup_bip39_recovery(self, num_words: int) -> BRGeneratorType:
|
||||||
if self.client.model is models.T2B1:
|
if self.client.model is models.T2B1:
|
||||||
yield from self.tr_recovery_homescreen()
|
yield from self.tr_recovery_homescreen()
|
||||||
|
@ -18,6 +18,7 @@ import pytest
|
|||||||
|
|
||||||
from trezorlib import device, models
|
from trezorlib import device, models
|
||||||
from trezorlib.debuglink import DebugLink
|
from trezorlib.debuglink import DebugLink
|
||||||
|
from trezorlib.messages import RecoveryStatus
|
||||||
|
|
||||||
from .. import buttons
|
from .. import buttons
|
||||||
from ..click_tests import recovery
|
from ..click_tests import recovery
|
||||||
@ -43,7 +44,7 @@ def test_abort(core_emulator: Emulator):
|
|||||||
if debug.model is models.T3T1:
|
if debug.model is models.T3T1:
|
||||||
pytest.skip("abort not supported on T3T1")
|
pytest.skip("abort not supported on T3T1")
|
||||||
|
|
||||||
assert features.recovery_mode is False
|
assert features.recovery_status == RecoveryStatus.NoRecovery
|
||||||
|
|
||||||
device_handler.run(device.recover, pin_protection=False)
|
device_handler.run(device.recover, pin_protection=False)
|
||||||
|
|
||||||
@ -55,7 +56,7 @@ def test_abort(core_emulator: Emulator):
|
|||||||
debug = _restart(device_handler, core_emulator)
|
debug = _restart(device_handler, core_emulator)
|
||||||
features = device_handler.features()
|
features = device_handler.features()
|
||||||
|
|
||||||
assert features.recovery_mode is True
|
assert features.recovery_status == RecoveryStatus.InNormalRecovery
|
||||||
|
|
||||||
# no waiting for layout because layout doesn't change
|
# no waiting for layout because layout doesn't change
|
||||||
assert "number of words" in debug.read_layout().text_content()
|
assert "number of words" in debug.read_layout().text_content()
|
||||||
@ -71,7 +72,7 @@ def test_abort(core_emulator: Emulator):
|
|||||||
|
|
||||||
assert layout.main_component() == "Homescreen"
|
assert layout.main_component() == "Homescreen"
|
||||||
features = device_handler.features()
|
features = device_handler.features()
|
||||||
assert features.recovery_mode is False
|
assert features.recovery_status == RecoveryStatus.NoRecovery
|
||||||
|
|
||||||
|
|
||||||
@core_only
|
@core_only
|
||||||
@ -81,7 +82,7 @@ def test_recovery_single_reset(core_emulator: Emulator):
|
|||||||
features = device_handler.features()
|
features = device_handler.features()
|
||||||
|
|
||||||
assert features.initialized is False
|
assert features.initialized is False
|
||||||
assert features.recovery_mode is False
|
assert features.recovery_status == RecoveryStatus.NoRecovery
|
||||||
|
|
||||||
device_handler.run(device.recover, pin_protection=False)
|
device_handler.run(device.recover, pin_protection=False)
|
||||||
|
|
||||||
@ -91,7 +92,7 @@ def test_recovery_single_reset(core_emulator: Emulator):
|
|||||||
|
|
||||||
debug = _restart(device_handler, core_emulator)
|
debug = _restart(device_handler, core_emulator)
|
||||||
features = device_handler.features()
|
features = device_handler.features()
|
||||||
assert features.recovery_mode is True
|
assert features.recovery_status == RecoveryStatus.InNormalRecovery
|
||||||
|
|
||||||
# we need to enter the number of words again, that's a feature
|
# we need to enter the number of words again, that's a feature
|
||||||
recovery.select_number_of_words(debug, wait=False)
|
recovery.select_number_of_words(debug, wait=False)
|
||||||
@ -100,7 +101,7 @@ def test_recovery_single_reset(core_emulator: Emulator):
|
|||||||
|
|
||||||
features = device_handler.features()
|
features = device_handler.features()
|
||||||
assert features.initialized is True
|
assert features.initialized is True
|
||||||
assert features.recovery_mode is False
|
assert features.recovery_status == RecoveryStatus.NoRecovery
|
||||||
|
|
||||||
|
|
||||||
@core_only
|
@core_only
|
||||||
@ -118,7 +119,7 @@ def test_recovery_on_old_wallet(core_emulator: Emulator):
|
|||||||
features = device_handler.features()
|
features = device_handler.features()
|
||||||
|
|
||||||
assert features.initialized is False
|
assert features.initialized is False
|
||||||
assert features.recovery_mode is False
|
assert features.recovery_status == RecoveryStatus.NoRecovery
|
||||||
|
|
||||||
# enter recovery mode
|
# enter recovery mode
|
||||||
device_handler.run(device.recover, pin_protection=False)
|
device_handler.run(device.recover, pin_protection=False)
|
||||||
@ -128,7 +129,7 @@ def test_recovery_on_old_wallet(core_emulator: Emulator):
|
|||||||
# restart to get into stand-alone recovery
|
# restart to get into stand-alone recovery
|
||||||
debug = _restart(device_handler, core_emulator)
|
debug = _restart(device_handler, core_emulator)
|
||||||
features = device_handler.features()
|
features = device_handler.features()
|
||||||
assert features.recovery_mode is True
|
assert features.recovery_status == RecoveryStatus.InNormalRecovery
|
||||||
|
|
||||||
# enter number of words
|
# enter number of words
|
||||||
recovery.select_number_of_words(debug, wait=False)
|
recovery.select_number_of_words(debug, wait=False)
|
||||||
@ -167,7 +168,7 @@ def test_recovery_on_old_wallet(core_emulator: Emulator):
|
|||||||
# check that the recovery succeeded
|
# check that the recovery succeeded
|
||||||
features = device_handler.features()
|
features = device_handler.features()
|
||||||
assert features.initialized is True
|
assert features.initialized is True
|
||||||
assert features.recovery_mode is False
|
assert features.recovery_status == RecoveryStatus.NoRecovery
|
||||||
|
|
||||||
|
|
||||||
@core_only
|
@core_only
|
||||||
@ -191,7 +192,7 @@ def test_recovery_multiple_resets(core_emulator: Emulator):
|
|||||||
features = device_handler.features()
|
features = device_handler.features()
|
||||||
|
|
||||||
assert features.initialized is False
|
assert features.initialized is False
|
||||||
assert features.recovery_mode is False
|
assert features.recovery_status == RecoveryStatus.NoRecovery
|
||||||
|
|
||||||
# start device and recovery
|
# start device and recovery
|
||||||
device_handler.run(device.recover, pin_protection=False)
|
device_handler.run(device.recover, pin_protection=False)
|
||||||
@ -204,7 +205,7 @@ def test_recovery_multiple_resets(core_emulator: Emulator):
|
|||||||
# restart
|
# restart
|
||||||
debug = _restart(device_handler, core_emulator)
|
debug = _restart(device_handler, core_emulator)
|
||||||
features = device_handler.features()
|
features = device_handler.features()
|
||||||
assert features.recovery_mode is True
|
assert features.recovery_status == RecoveryStatus.InNormalRecovery
|
||||||
|
|
||||||
# enter the number of words again, that's a feature!
|
# enter the number of words again, that's a feature!
|
||||||
recovery.select_number_of_words(debug, wait=False)
|
recovery.select_number_of_words(debug, wait=False)
|
||||||
@ -216,4 +217,4 @@ def test_recovery_multiple_resets(core_emulator: Emulator):
|
|||||||
|
|
||||||
features = device_handler.features()
|
features = device_handler.features()
|
||||||
assert features.initialized is True
|
assert features.initialized is True
|
||||||
assert features.recovery_mode is False
|
assert features.recovery_status == RecoveryStatus.NoRecovery
|
||||||
|
@ -90,10 +90,16 @@ def _resolve_path_to_texts(
|
|||||||
texts: list[str] = []
|
texts: list[str] = []
|
||||||
lookups = path.split(".")
|
lookups = path.split(".")
|
||||||
for language_data in all_language_data:
|
for language_data in all_language_data:
|
||||||
|
language_data_missing = False
|
||||||
data: dict[str, t.Any] | str = language_data
|
data: dict[str, t.Any] | str = language_data
|
||||||
for lookup in lookups:
|
for lookup in lookups:
|
||||||
assert isinstance(data, dict), f"{lookup} is not a dict"
|
assert isinstance(data, dict), f"{lookup} is not a dict"
|
||||||
|
if lookup not in data:
|
||||||
|
language_data_missing = True
|
||||||
|
break
|
||||||
data = data[lookup]
|
data = data[lookup]
|
||||||
|
if language_data_missing:
|
||||||
|
continue
|
||||||
assert isinstance(data, str), f"{path} is not a string"
|
assert isinstance(data, str), f"{path} is not a string"
|
||||||
if template:
|
if template:
|
||||||
data = data.format(*template)
|
data = data.format(*template)
|
||||||
|
@ -864,6 +864,9 @@
|
|||||||
"T2B1_en_test_recovery.py::test_recovery_bip39": "fd055e526b66f7d0eb3b6968f3889eb4a3e589fd3700b6688e68ea5d6c0c18dd",
|
"T2B1_en_test_recovery.py::test_recovery_bip39": "fd055e526b66f7d0eb3b6968f3889eb4a3e589fd3700b6688e68ea5d6c0c18dd",
|
||||||
"T2B1_en_test_recovery.py::test_recovery_bip39_previous_word": "10c4d76d4f636878540dcb6ba258a2d28489012298cf273af3808bcadbe38754",
|
"T2B1_en_test_recovery.py::test_recovery_bip39_previous_word": "10c4d76d4f636878540dcb6ba258a2d28489012298cf273af3808bcadbe38754",
|
||||||
"T2B1_en_test_recovery.py::test_recovery_slip39_basic": "771d9bd01496b0fea72d5faca60fcc564e83d4a69e5fe02770238071dec7274d",
|
"T2B1_en_test_recovery.py::test_recovery_slip39_basic": "771d9bd01496b0fea72d5faca60fcc564e83d4a69e5fe02770238071dec7274d",
|
||||||
|
"T2B1_en_test_repeated_backup.py::test_repeated_backup": "cbdb2fc033538e4995a9ba5fc658a2e58936c209e5165244f3f13d65de7125bd",
|
||||||
|
"T2B1_en-test_repeated_backup.py::test_repeated_backup_cancel": "902b8f47380e0591bf3d5e410a9db86fe1a3b8221c968b89c97960fdd91e5100",
|
||||||
|
"T2B1_en-test_repeated_backup.py::test_repeated_backup_send_disallowed_message": "660c0dd0c290f8320d0194c6df2c44c9741a7f05414c0afddad0b23b95c0906d",
|
||||||
"T2B1_en_test_reset_bip39.py::test_reset_bip39": "6d229eb212360ffad2bc72154588ec82f699ae6d0cbbf87fa950dd4c8e2ea9c9",
|
"T2B1_en_test_reset_bip39.py::test_reset_bip39": "6d229eb212360ffad2bc72154588ec82f699ae6d0cbbf87fa950dd4c8e2ea9c9",
|
||||||
"T2B1_en_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "5cd7eb5b96673f7f9dc066d2860ef4ab42926488a5117a2bc89caa667c3916e1",
|
"T2B1_en_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "5cd7eb5b96673f7f9dc066d2860ef4ab42926488a5117a2bc89caa667c3916e1",
|
||||||
"T2B1_en_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "cb815314c0607f0d6f678f5a9b9b99d6e50cbb670a83b3463ad2bf8635f9319a",
|
"T2B1_en_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "cb815314c0607f0d6f678f5a9b9b99d6e50cbb670a83b3463ad2bf8635f9319a",
|
||||||
@ -7995,6 +7998,7 @@
|
|||||||
"T2T1_en_test_recovery.py::test_recovery_bip39": "ee619ed8e0cf0fbd75a685830d1873e23896dcca6477b2bbf39eaa8d9b4c8531",
|
"T2T1_en_test_recovery.py::test_recovery_bip39": "ee619ed8e0cf0fbd75a685830d1873e23896dcca6477b2bbf39eaa8d9b4c8531",
|
||||||
"T2T1_en_test_recovery.py::test_recovery_bip39_previous_word": "b41a576c6e879512c80337f5d58fdaa93d2259ed42f164f808e52e0d3ccf1211",
|
"T2T1_en_test_recovery.py::test_recovery_bip39_previous_word": "b41a576c6e879512c80337f5d58fdaa93d2259ed42f164f808e52e0d3ccf1211",
|
||||||
"T2T1_en_test_recovery.py::test_recovery_slip39_basic": "81bc28e27af3667606f0479720e4dbdfdb97805b9d94a6f3dfe9d9cb3b045c65",
|
"T2T1_en_test_recovery.py::test_recovery_slip39_basic": "81bc28e27af3667606f0479720e4dbdfdb97805b9d94a6f3dfe9d9cb3b045c65",
|
||||||
|
"T2T1_en_test_repeated_backup.py::test_repeated_backup": "4ee56b338d37f74f892ad80e3ad35bb8f9113b4de602b57136899f52711ac0d9",
|
||||||
"T2T1_en_test_reset_bip39.py::test_reset_bip39": "e7d4a85f9ebfaec35006a566e0a25167f276620d50d847a08721da861cac725a",
|
"T2T1_en_test_reset_bip39.py::test_reset_bip39": "e7d4a85f9ebfaec35006a566e0a25167f276620d50d847a08721da861cac725a",
|
||||||
"T2T1_en_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "259315387e0837ac718654d9180f5730370fa76d77318d620288885ebbddb0fb",
|
"T2T1_en_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "259315387e0837ac718654d9180f5730370fa76d77318d620288885ebbddb0fb",
|
||||||
"T2T1_en_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "878b828c11353fbfcd854fd54ed172dc7d395c753195d5444b5b57e3499e2bca",
|
"T2T1_en_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "878b828c11353fbfcd854fd54ed172dc7d395c753195d5444b5b57e3499e2bca",
|
||||||
@ -12447,6 +12451,9 @@
|
|||||||
"T2T1_en_test_protection_levels.py::test_unlocked": "4488e2b6f06fdff05749ac271d080182f1c95645de37898457ff12f0fb190381",
|
"T2T1_en_test_protection_levels.py::test_unlocked": "4488e2b6f06fdff05749ac271d080182f1c95645de37898457ff12f0fb190381",
|
||||||
"T2T1_en_test_protection_levels.py::test_verify_message_t2": "cc09f0acf9e48b1355dae0be65a2a97abbe2811808feee08d46ae7146ce6bb6f",
|
"T2T1_en_test_protection_levels.py::test_verify_message_t2": "cc09f0acf9e48b1355dae0be65a2a97abbe2811808feee08d46ae7146ce6bb6f",
|
||||||
"T2T1_en_test_protection_levels.py::test_wipe_device": "7ff017de646b7cf70832605e1750c635d0eb661d51534b56007b49e82b927011",
|
"T2T1_en_test_protection_levels.py::test_wipe_device": "7ff017de646b7cf70832605e1750c635d0eb661d51534b56007b49e82b927011",
|
||||||
|
"T2T1_en_test_repeated_backup.py::test_repeated_backup": "321a6c7a274e20fbddcc0dbdff31181950a613c97a77ad48ce1bb31845ffdc0e",
|
||||||
|
"T2T1_en_test_repeated_backup.py::test_repeated_backup_cancel": "9ab2f1f9e49582efa1e98c728b38ce1a21c4f09d398b20f616507f67a6a292b2",
|
||||||
|
"T2T1_en_test_repeated_backup.py::test_repeated_backup_send_disallowed_message": "dc906965730138e588e4efb5e7eb71609c504f5910352ce04bd926358184af36",
|
||||||
"T2T1_en_test_sdcard.py::test_sd_format": "83d0d9b4eab3bf0eafc22d7d95e8b70bce477ba9c8b4ba13eeca9380ad5fdafa",
|
"T2T1_en_test_sdcard.py::test_sd_format": "83d0d9b4eab3bf0eafc22d7d95e8b70bce477ba9c8b4ba13eeca9380ad5fdafa",
|
||||||
"T2T1_en_test_sdcard.py::test_sd_no_format": "14511e3d3ee535d97287d8ade25101e8c16db17c1dc5d3151b91e5e8eba61ba5",
|
"T2T1_en_test_sdcard.py::test_sd_no_format": "14511e3d3ee535d97287d8ade25101e8c16db17c1dc5d3151b91e5e8eba61ba5",
|
||||||
"T2T1_en_test_sdcard.py::test_sd_protect_unlock": "1302f9a0835cac621142a17031d2150553e676261a3eeeefd32fcf1e69c7bd1a",
|
"T2T1_en_test_sdcard.py::test_sd_protect_unlock": "1302f9a0835cac621142a17031d2150553e676261a3eeeefd32fcf1e69c7bd1a",
|
||||||
|
@ -21,7 +21,7 @@ import pytest
|
|||||||
from shamir_mnemonic import shamir
|
from shamir_mnemonic import shamir
|
||||||
|
|
||||||
from trezorlib import btc, debuglink, device, exceptions, fido, models
|
from trezorlib import btc, debuglink, device, exceptions, fido, models
|
||||||
from trezorlib.messages import ApplySettings, BackupType, Success
|
from trezorlib.messages import ApplySettings, BackupType, RecoveryStatus, Success
|
||||||
from trezorlib.tools import H_
|
from trezorlib.tools import H_
|
||||||
|
|
||||||
from ..common import MNEMONIC_SLIP39_BASIC_20_3of6, MNEMONIC_SLIP39_BASIC_20_3of6_SECRET
|
from ..common import MNEMONIC_SLIP39_BASIC_20_3of6, MNEMONIC_SLIP39_BASIC_20_3of6_SECRET
|
||||||
@ -291,7 +291,7 @@ def test_upgrade_shamir_recovery(gen: str, tag: Optional[str]):
|
|||||||
with EmulatorWrapper(gen, tag) as emu, BackgroundDeviceHandler(
|
with EmulatorWrapper(gen, tag) as emu, BackgroundDeviceHandler(
|
||||||
emu.client
|
emu.client
|
||||||
) as device_handler:
|
) as device_handler:
|
||||||
assert emu.client.features.recovery_mode is False
|
assert emu.client.features.recovery_status == RecoveryStatus.NoRecovery
|
||||||
emu.client.watch_layout(True)
|
emu.client.watch_layout(True)
|
||||||
debug = device_handler.debuglink()
|
debug = device_handler.debuglink()
|
||||||
|
|
||||||
@ -312,7 +312,7 @@ def test_upgrade_shamir_recovery(gen: str, tag: Optional[str]):
|
|||||||
|
|
||||||
with EmulatorWrapper(gen, storage=storage) as emu:
|
with EmulatorWrapper(gen, storage=storage) as emu:
|
||||||
assert device_id == emu.client.features.device_id
|
assert device_id == emu.client.features.device_id
|
||||||
assert emu.client.features.recovery_mode
|
assert emu.client.features.recovery_status == RecoveryStatus.InNormalRecovery
|
||||||
debug = emu.client.debug
|
debug = emu.client.debug
|
||||||
emu.client.watch_layout(True)
|
emu.client.watch_layout(True)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user