1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-08-02 11:58:32 +00:00

test(core/sdbackup): reset, recovery

- BIP39 recovery test (MNEMONIC12 backup block written directly to
  mocked SD card)
- BIP39 reset-recovery test
- 3-of-5 shamir reset-recovery test
- WIP: InputFlows will be updated and polished with UX improvements later
This commit is contained in:
obrusvit 2024-01-16 11:01:04 +01:00
parent a0ef80baeb
commit 144a4e812d
14 changed files with 598 additions and 46 deletions

View File

@ -190,9 +190,14 @@ message DebugLinkEraseSdCard {
* @next Success * @next Success
*/ */
message DebugLinkInsertSdCard { message DebugLinkInsertSdCard {
optional uint32 serial_number = 1; // mocked serial number of the card (e.g.: 1, 2, 3,...) optional uint32 serial_number = 1; // mocked serial number of the card (range 1-16)
optional uint32 capacity_bytes = 2; // capacity of the card in bytes optional uint32 capacity_bytes = 2; // capacity of the mocked card in bytes
optional uint32 manuf_ID = 3; // mocked manufacturer ID optional uint32 manuf_ID = 3; // mocked manufacturer ID
repeated DebugLinkSdCardDataBlock data_blocks = 4; // data written directly to individual memory blocks
message DebugLinkSdCardDataBlock {
required uint32 number = 1;
required bytes data = 2;
}
} }
/** /**
@ -205,7 +210,6 @@ message DebugLinkWatchLayout {
// if false, stop. // if false, stop.
} }
/** /**
* Request: Remove all the previous debug event state * Request: Remove all the previous debug event state
* @start * @start

View File

@ -5,7 +5,7 @@ from typing import *
def insert( def insert(
card_sn: int, card_sn: int,
capacity_bytes: int | None = 122_945_536, capacity_bytes: int | None = 122_945_536,
manuf_id: int | None = 27, manuf_id: int | None = 39,
) -> None: ) -> None:
""" """
Inserts SD card to the emulator. Inserts SD card to the emulator.

View File

@ -241,15 +241,14 @@ if __debug__:
try: try:
sdcard.power_on() sdcard.power_on()
# trash the whole card
assert sdcard.capacity() >= sdcard.BLOCK_SIZE
empty_block = bytes([0xFF] * sdcard.BLOCK_SIZE)
for i in range(sdcard.capacity() // sdcard.BLOCK_SIZE):
sdcard.write(i, empty_block)
# make filesystem
if msg.format: if msg.format:
io.fatfs.mkfs() io.fatfs.mkfs()
else:
# trash first 1 MB of data to destroy the FAT filesystem
assert sdcard.capacity() >= 1024 * 1024
empty_block = bytes([0xFF] * sdcard.BLOCK_SIZE)
for i in range(1024 * 1024 // sdcard.BLOCK_SIZE):
sdcard.write(i, empty_block)
except OSError: except OSError:
raise wire.ProcessError("SD card operation failed") raise wire.ProcessError("SD card operation failed")
finally: finally:
@ -268,6 +267,12 @@ if __debug__:
capacity_bytes=msg.capacity_bytes, capacity_bytes=msg.capacity_bytes,
manuf_id=msg.manuf_ID, manuf_id=msg.manuf_ID,
) )
if msg.data_blocks is not None:
sdcard = io.sdcard
sdcard.power_on()
for block in msg.data_blocks:
sdcard.write(block.number, block.data)
sdcard.power_off()
return Success() return Success()

View File

@ -2927,10 +2927,12 @@ if TYPE_CHECKING:
serial_number: "int | None" serial_number: "int | None"
capacity_bytes: "int | None" capacity_bytes: "int | None"
manuf_ID: "int | None" manuf_ID: "int | None"
data_blocks: "list[DebugLinkSdCardDataBlock]"
def __init__( def __init__(
self, self,
*, *,
data_blocks: "list[DebugLinkSdCardDataBlock] | None" = None,
serial_number: "int | None" = None, serial_number: "int | None" = None,
capacity_bytes: "int | None" = None, capacity_bytes: "int | None" = None,
manuf_ID: "int | None" = None, manuf_ID: "int | None" = None,
@ -2961,6 +2963,22 @@ if TYPE_CHECKING:
def is_type_of(cls, msg: Any) -> TypeGuard["DebugLinkResetDebugEvents"]: def is_type_of(cls, msg: Any) -> TypeGuard["DebugLinkResetDebugEvents"]:
return isinstance(msg, cls) return isinstance(msg, cls)
class DebugLinkSdCardDataBlock(protobuf.MessageType):
number: "int"
data: "bytes"
def __init__(
self,
*,
number: "int",
data: "bytes",
) -> None:
pass
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["DebugLinkSdCardDataBlock"]:
return isinstance(msg, cls)
class EosGetPublicKey(protobuf.MessageType): class EosGetPublicKey(protobuf.MessageType):
address_n: "list[int]" address_n: "list[int]"
show_display: "bool | None" show_display: "bool | None"

View File

@ -25,7 +25,7 @@ if TYPE_CHECKING:
P = ParamSpec("P") P = ParamSpec("P")
R = TypeVar("R") R = TypeVar("R")
SD_CARD_HOT_SWAPPABLE = False SD_CARD_HOT_SWAPPABLE = True
class FilesystemWrapper: class FilesystemWrapper:

View File

@ -746,6 +746,7 @@ class DebugLink:
serial_number: int = 1, serial_number: int = 1,
capacity_bytes: Optional[int] = None, capacity_bytes: Optional[int] = None,
manuf_ID: Optional[int] = None, manuf_ID: Optional[int] = None,
data_blocks: Optional[List[messages.DebugLinkSdCardDataBlock]] = None,
) -> messages.Success: ) -> messages.Success:
if not self.model == "T": if not self.model == "T":
raise RuntimeError("SD card not supported by this device.") raise RuntimeError("SD card not supported by this device.")
@ -756,6 +757,7 @@ class DebugLink:
serial_number=serial_number, serial_number=serial_number,
capacity_bytes=capacity_bytes, capacity_bytes=capacity_bytes,
manuf_ID=manuf_ID, manuf_ID=manuf_ID,
data_blocks=data_blocks,
) )
) )

View File

@ -4091,15 +4091,18 @@ class DebugLinkInsertSdCard(protobuf.MessageType):
1: protobuf.Field("serial_number", "uint32", repeated=False, required=False, default=None), 1: protobuf.Field("serial_number", "uint32", repeated=False, required=False, default=None),
2: protobuf.Field("capacity_bytes", "uint32", repeated=False, required=False, default=None), 2: protobuf.Field("capacity_bytes", "uint32", repeated=False, required=False, default=None),
3: protobuf.Field("manuf_ID", "uint32", repeated=False, required=False, default=None), 3: protobuf.Field("manuf_ID", "uint32", repeated=False, required=False, default=None),
4: protobuf.Field("data_blocks", "DebugLinkSdCardDataBlock", repeated=True, required=False, default=None),
} }
def __init__( def __init__(
self, self,
*, *,
data_blocks: Optional[Sequence["DebugLinkSdCardDataBlock"]] = None,
serial_number: Optional["int"] = None, serial_number: Optional["int"] = None,
capacity_bytes: Optional["int"] = None, capacity_bytes: Optional["int"] = None,
manuf_ID: Optional["int"] = None, manuf_ID: Optional["int"] = None,
) -> None: ) -> None:
self.data_blocks: Sequence["DebugLinkSdCardDataBlock"] = data_blocks if data_blocks is not None else []
self.serial_number = serial_number self.serial_number = serial_number
self.capacity_bytes = capacity_bytes self.capacity_bytes = capacity_bytes
self.manuf_ID = manuf_ID self.manuf_ID = manuf_ID
@ -4123,6 +4126,23 @@ class DebugLinkResetDebugEvents(protobuf.MessageType):
MESSAGE_WIRE_TYPE = 9008 MESSAGE_WIRE_TYPE = 9008
class DebugLinkSdCardDataBlock(protobuf.MessageType):
MESSAGE_WIRE_TYPE = None
FIELDS = {
1: protobuf.Field("number", "uint32", repeated=False, required=True),
2: protobuf.Field("data", "bytes", repeated=False, required=True),
}
def __init__(
self,
*,
number: "int",
data: "bytes",
) -> None:
self.number = number
self.data = data
class EosGetPublicKey(protobuf.MessageType): class EosGetPublicKey(protobuf.MessageType):
MESSAGE_WIRE_TYPE = 600 MESSAGE_WIRE_TYPE = 600
FIELDS = { FIELDS = {

View File

@ -3220,6 +3220,8 @@ pub struct DebugLinkInsertSdCard {
pub capacity_bytes: ::std::option::Option<u32>, pub capacity_bytes: ::std::option::Option<u32>,
// @@protoc_insertion_point(field:hw.trezor.messages.debug.DebugLinkInsertSdCard.manuf_ID) // @@protoc_insertion_point(field:hw.trezor.messages.debug.DebugLinkInsertSdCard.manuf_ID)
pub manuf_ID: ::std::option::Option<u32>, pub manuf_ID: ::std::option::Option<u32>,
// @@protoc_insertion_point(field:hw.trezor.messages.debug.DebugLinkInsertSdCard.data_blocks)
pub data_blocks: ::std::vec::Vec<debug_link_insert_sd_card::DebugLinkSdCardDataBlock>,
// special fields // special fields
// @@protoc_insertion_point(special_field:hw.trezor.messages.debug.DebugLinkInsertSdCard.special_fields) // @@protoc_insertion_point(special_field:hw.trezor.messages.debug.DebugLinkInsertSdCard.special_fields)
pub special_fields: ::protobuf::SpecialFields, pub special_fields: ::protobuf::SpecialFields,
@ -3294,7 +3296,7 @@ impl DebugLinkInsertSdCard {
} }
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
let mut fields = ::std::vec::Vec::with_capacity(3); let mut fields = ::std::vec::Vec::with_capacity(4);
let mut oneofs = ::std::vec::Vec::with_capacity(0); let mut oneofs = ::std::vec::Vec::with_capacity(0);
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
"serial_number", "serial_number",
@ -3311,6 +3313,11 @@ impl DebugLinkInsertSdCard {
|m: &DebugLinkInsertSdCard| { &m.manuf_ID }, |m: &DebugLinkInsertSdCard| { &m.manuf_ID },
|m: &mut DebugLinkInsertSdCard| { &mut m.manuf_ID }, |m: &mut DebugLinkInsertSdCard| { &mut m.manuf_ID },
)); ));
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
"data_blocks",
|m: &DebugLinkInsertSdCard| { &m.data_blocks },
|m: &mut DebugLinkInsertSdCard| { &mut m.data_blocks },
));
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<DebugLinkInsertSdCard>( ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<DebugLinkInsertSdCard>(
"DebugLinkInsertSdCard", "DebugLinkInsertSdCard",
fields, fields,
@ -3323,6 +3330,11 @@ impl ::protobuf::Message for DebugLinkInsertSdCard {
const NAME: &'static str = "DebugLinkInsertSdCard"; const NAME: &'static str = "DebugLinkInsertSdCard";
fn is_initialized(&self) -> bool { fn is_initialized(&self) -> bool {
for v in &self.data_blocks {
if !v.is_initialized() {
return false;
}
};
true true
} }
@ -3338,6 +3350,9 @@ impl ::protobuf::Message for DebugLinkInsertSdCard {
24 => { 24 => {
self.manuf_ID = ::std::option::Option::Some(is.read_uint32()?); self.manuf_ID = ::std::option::Option::Some(is.read_uint32()?);
}, },
34 => {
self.data_blocks.push(is.read_message()?);
},
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())?;
}, },
@ -3359,6 +3374,10 @@ impl ::protobuf::Message for DebugLinkInsertSdCard {
if let Some(v) = self.manuf_ID { if let Some(v) = self.manuf_ID {
my_size += ::protobuf::rt::uint32_size(3, v); my_size += ::protobuf::rt::uint32_size(3, v);
} }
for value in &self.data_blocks {
let len = value.compute_size();
my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
};
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);
my_size my_size
@ -3374,6 +3393,9 @@ impl ::protobuf::Message for DebugLinkInsertSdCard {
if let Some(v) = self.manuf_ID { if let Some(v) = self.manuf_ID {
os.write_uint32(3, v)?; os.write_uint32(3, v)?;
} }
for v in &self.data_blocks {
::protobuf::rt::write_message_field_with_cached_size(4, v, os)?;
};
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(())
} }
@ -3394,6 +3416,7 @@ impl ::protobuf::Message for DebugLinkInsertSdCard {
self.serial_number = ::std::option::Option::None; self.serial_number = ::std::option::Option::None;
self.capacity_bytes = ::std::option::Option::None; self.capacity_bytes = ::std::option::Option::None;
self.manuf_ID = ::std::option::Option::None; self.manuf_ID = ::std::option::Option::None;
self.data_blocks.clear();
self.special_fields.clear(); self.special_fields.clear();
} }
@ -3402,6 +3425,7 @@ impl ::protobuf::Message for DebugLinkInsertSdCard {
serial_number: ::std::option::Option::None, serial_number: ::std::option::Option::None,
capacity_bytes: ::std::option::Option::None, capacity_bytes: ::std::option::Option::None,
manuf_ID: ::std::option::Option::None, manuf_ID: ::std::option::Option::None,
data_blocks: ::std::vec::Vec::new(),
special_fields: ::protobuf::SpecialFields::new(), special_fields: ::protobuf::SpecialFields::new(),
}; };
&instance &instance
@ -3425,6 +3449,210 @@ impl ::protobuf::reflect::ProtobufValue for DebugLinkInsertSdCard {
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
} }
/// Nested message and enums of message `DebugLinkInsertSdCard`
pub mod debug_link_insert_sd_card {
// @@protoc_insertion_point(message:hw.trezor.messages.debug.DebugLinkInsertSdCard.DebugLinkSdCardDataBlock)
#[derive(PartialEq,Clone,Default,Debug)]
pub struct DebugLinkSdCardDataBlock {
// message fields
// @@protoc_insertion_point(field:hw.trezor.messages.debug.DebugLinkInsertSdCard.DebugLinkSdCardDataBlock.number)
pub number: ::std::option::Option<u32>,
// @@protoc_insertion_point(field:hw.trezor.messages.debug.DebugLinkInsertSdCard.DebugLinkSdCardDataBlock.data)
pub data: ::std::option::Option<::std::vec::Vec<u8>>,
// special fields
// @@protoc_insertion_point(special_field:hw.trezor.messages.debug.DebugLinkInsertSdCard.DebugLinkSdCardDataBlock.special_fields)
pub special_fields: ::protobuf::SpecialFields,
}
impl<'a> ::std::default::Default for &'a DebugLinkSdCardDataBlock {
fn default() -> &'a DebugLinkSdCardDataBlock {
<DebugLinkSdCardDataBlock as ::protobuf::Message>::default_instance()
}
}
impl DebugLinkSdCardDataBlock {
pub fn new() -> DebugLinkSdCardDataBlock {
::std::default::Default::default()
}
// required uint32 number = 1;
pub fn number(&self) -> u32 {
self.number.unwrap_or(0)
}
pub fn clear_number(&mut self) {
self.number = ::std::option::Option::None;
}
pub fn has_number(&self) -> bool {
self.number.is_some()
}
// Param is passed by value, moved
pub fn set_number(&mut self, v: u32) {
self.number = ::std::option::Option::Some(v);
}
// required bytes data = 2;
pub fn data(&self) -> &[u8] {
match self.data.as_ref() {
Some(v) => v,
None => &[],
}
}
pub fn clear_data(&mut self) {
self.data = ::std::option::Option::None;
}
pub fn has_data(&self) -> bool {
self.data.is_some()
}
// Param is passed by value, moved
pub fn set_data(&mut self, v: ::std::vec::Vec<u8>) {
self.data = ::std::option::Option::Some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_data(&mut self) -> &mut ::std::vec::Vec<u8> {
if self.data.is_none() {
self.data = ::std::option::Option::Some(::std::vec::Vec::new());
}
self.data.as_mut().unwrap()
}
// Take field
pub fn take_data(&mut self) -> ::std::vec::Vec<u8> {
self.data.take().unwrap_or_else(|| ::std::vec::Vec::new())
}
pub(in super) fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
let mut fields = ::std::vec::Vec::with_capacity(2);
let mut oneofs = ::std::vec::Vec::with_capacity(0);
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
"number",
|m: &DebugLinkSdCardDataBlock| { &m.number },
|m: &mut DebugLinkSdCardDataBlock| { &mut m.number },
));
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
"data",
|m: &DebugLinkSdCardDataBlock| { &m.data },
|m: &mut DebugLinkSdCardDataBlock| { &mut m.data },
));
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<DebugLinkSdCardDataBlock>(
"DebugLinkInsertSdCard.DebugLinkSdCardDataBlock",
fields,
oneofs,
)
}
}
impl ::protobuf::Message for DebugLinkSdCardDataBlock {
const NAME: &'static str = "DebugLinkSdCardDataBlock";
fn is_initialized(&self) -> bool {
if self.number.is_none() {
return false;
}
if self.data.is_none() {
return false;
}
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
while let Some(tag) = is.read_raw_tag_or_eof()? {
match tag {
8 => {
self.number = ::std::option::Option::Some(is.read_uint32()?);
},
18 => {
self.data = ::std::option::Option::Some(is.read_bytes()?);
},
tag => {
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u64 {
let mut my_size = 0;
if let Some(v) = self.number {
my_size += ::protobuf::rt::uint32_size(1, v);
}
if let Some(v) = self.data.as_ref() {
my_size += ::protobuf::rt::bytes_size(2, &v);
}
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
self.special_fields.cached_size().set(my_size as u32);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
if let Some(v) = self.number {
os.write_uint32(1, v)?;
}
if let Some(v) = self.data.as_ref() {
os.write_bytes(2, v)?;
}
os.write_unknown_fields(self.special_fields.unknown_fields())?;
::std::result::Result::Ok(())
}
fn special_fields(&self) -> &::protobuf::SpecialFields {
&self.special_fields
}
fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
&mut self.special_fields
}
fn new() -> DebugLinkSdCardDataBlock {
DebugLinkSdCardDataBlock::new()
}
fn clear(&mut self) {
self.number = ::std::option::Option::None;
self.data = ::std::option::Option::None;
self.special_fields.clear();
}
fn default_instance() -> &'static DebugLinkSdCardDataBlock {
static instance: DebugLinkSdCardDataBlock = DebugLinkSdCardDataBlock {
number: ::std::option::Option::None,
data: ::std::option::Option::None,
special_fields: ::protobuf::SpecialFields::new(),
};
&instance
}
}
impl ::protobuf::MessageFull for DebugLinkSdCardDataBlock {
fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
descriptor.get(|| super::file_descriptor().message_by_package_relative_name("DebugLinkInsertSdCard.DebugLinkSdCardDataBlock").unwrap()).clone()
}
}
impl ::std::fmt::Display for DebugLinkSdCardDataBlock {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for DebugLinkSdCardDataBlock {
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
}
}
// @@protoc_insertion_point(message:hw.trezor.messages.debug.DebugLinkWatchLayout) // @@protoc_insertion_point(message:hw.trezor.messages.debug.DebugLinkWatchLayout)
#[derive(PartialEq,Clone,Default,Debug)] #[derive(PartialEq,Clone,Default,Debug)]
pub struct DebugLinkWatchLayout { pub struct DebugLinkWatchLayout {
@ -3714,13 +3942,17 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x01(\rR\x07address\x12\x16\n\x06memory\x18\x02\x20\x01(\x0cR\x06memory\ \x01(\rR\x07address\x12\x16\n\x06memory\x18\x02\x20\x01(\x0cR\x06memory\
\x12\x14\n\x05flash\x18\x03\x20\x01(\x08R\x05flash\"-\n\x13DebugLinkFlas\ \x12\x14\n\x05flash\x18\x03\x20\x01(\x08R\x05flash\"-\n\x13DebugLinkFlas\
hErase\x12\x16\n\x06sector\x18\x01\x20\x01(\rR\x06sector\".\n\x14DebugLi\ hErase\x12\x16\n\x06sector\x18\x01\x20\x01(\rR\x06sector\".\n\x14DebugLi\
nkEraseSdCard\x12\x16\n\x06format\x18\x01\x20\x01(\x08R\x06format\"~\n\ nkEraseSdCard\x12\x16\n\x06format\x18\x01\x20\x01(\x08R\x06format\"\xb1\
\x15DebugLinkInsertSdCard\x12#\n\rserial_number\x18\x01\x20\x01(\rR\x0cs\ \x02\n\x15DebugLinkInsertSdCard\x12#\n\rserial_number\x18\x01\x20\x01(\r\
erialNumber\x12%\n\x0ecapacity_bytes\x18\x02\x20\x01(\rR\rcapacityBytes\ R\x0cserialNumber\x12%\n\x0ecapacity_bytes\x18\x02\x20\x01(\rR\rcapacity\
\x12\x19\n\x08manuf_ID\x18\x03\x20\x01(\rR\x07manufID\",\n\x14DebugLinkW\ Bytes\x12\x19\n\x08manuf_ID\x18\x03\x20\x01(\rR\x07manufID\x12i\n\x0bdat\
atchLayout\x12\x14\n\x05watch\x18\x01\x20\x01(\x08R\x05watch\"\x1b\n\x19\ a_blocks\x18\x04\x20\x03(\x0b2H.hw.trezor.messages.debug.DebugLinkInsert\
DebugLinkResetDebugEventsB=\n#com.satoshilabs.trezor.lib.protobufB\x12Tr\ SdCard.DebugLinkSdCardDataBlockR\ndataBlocks\x1aF\n\x18DebugLinkSdCardDa\
ezorMessageDebug\x80\xa6\x1d\x01\ taBlock\x12\x16\n\x06number\x18\x01\x20\x02(\rR\x06number\x12\x12\n\x04d\
ata\x18\x02\x20\x02(\x0cR\x04data\",\n\x14DebugLinkWatchLayout\x12\x14\n\
\x05watch\x18\x01\x20\x01(\x08R\x05watch\"\x1b\n\x19DebugLinkResetDebugE\
ventsB=\n#com.satoshilabs.trezor.lib.protobufB\x12TrezorMessageDebug\x80\
\xa6\x1d\x01\
"; ";
/// `FileDescriptorProto` object which was a source for this generated file /// `FileDescriptorProto` object which was a source for this generated file
@ -3741,7 +3973,7 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor {
deps.push(super::messages::file_descriptor().clone()); deps.push(super::messages::file_descriptor().clone());
deps.push(super::messages_common::file_descriptor().clone()); deps.push(super::messages_common::file_descriptor().clone());
deps.push(super::messages_management::file_descriptor().clone()); deps.push(super::messages_management::file_descriptor().clone());
let mut messages = ::std::vec::Vec::with_capacity(16); let mut messages = ::std::vec::Vec::with_capacity(17);
messages.push(DebugLinkDecision::generated_message_descriptor_data()); messages.push(DebugLinkDecision::generated_message_descriptor_data());
messages.push(DebugLinkLayout::generated_message_descriptor_data()); messages.push(DebugLinkLayout::generated_message_descriptor_data());
messages.push(DebugLinkReseedRandom::generated_message_descriptor_data()); messages.push(DebugLinkReseedRandom::generated_message_descriptor_data());
@ -3758,6 +3990,7 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor {
messages.push(DebugLinkInsertSdCard::generated_message_descriptor_data()); messages.push(DebugLinkInsertSdCard::generated_message_descriptor_data());
messages.push(DebugLinkWatchLayout::generated_message_descriptor_data()); messages.push(DebugLinkWatchLayout::generated_message_descriptor_data());
messages.push(DebugLinkResetDebugEvents::generated_message_descriptor_data()); messages.push(DebugLinkResetDebugEvents::generated_message_descriptor_data());
messages.push(debug_link_insert_sd_card::DebugLinkSdCardDataBlock::generated_message_descriptor_data());
let mut enums = ::std::vec::Vec::with_capacity(3); let mut enums = ::std::vec::Vec::with_capacity(3);
enums.push(debug_link_decision::DebugSwipeDirection::generated_enum_descriptor_data()); enums.push(debug_link_decision::DebugSwipeDirection::generated_enum_descriptor_data());
enums.push(debug_link_decision::DebugButton::generated_enum_descriptor_data()); enums.push(debug_link_decision::DebugButton::generated_enum_descriptor_data());

View File

@ -215,6 +215,7 @@ def client(
# we need to reseed before the wipe # we need to reseed before the wipe
_raw_client.debug.reseed(0) _raw_client.debug.reseed(0)
# Insert SD card if needed
sd_marker = request.node.get_closest_marker("sd_card") sd_marker = request.node.get_closest_marker("sd_card")
if sd_marker: if sd_marker:
_raw_client.debug.insert_sd_card(1) _raw_client.debug.insert_sd_card(1)

View File

@ -1,34 +1,44 @@
import pytest import pytest
from trezorlib import device from trezorlib import device, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.messages import BackupType from trezorlib.messages import BackupType
from ...common import MNEMONIC12
from ...input_flows import InputFlowBip39RecoverySdCard
pytestmark = [pytest.mark.skip_t1, pytest.mark.skip_tr] pytestmark = [pytest.mark.skip_t1, pytest.mark.skip_tr]
def prepare_data_for_sdcard() -> bytes:
# MNEMONIC12 backup block
backup_block_str = "54525A4D000000004C616C636F686F6C20776F6D616E206162757365206D75737420647572696E67206D6F6E69746F72206E6F626C652061637475616C206D6978656420747261646520616E676572206169736C654B1118DAD99C3A21E85AC1CBAE3D41F8BA02BE5E6B8422B3225C9DB53C316D8A0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
return bytes.fromhex(backup_block_str)
@pytest.mark.setup_client(uninitialized=True) @pytest.mark.setup_client(uninitialized=True)
@pytest.mark.sd_card(formatted=False) @pytest.mark.sd_card(formatted=False)
def test_sd_backup_end_to_end(client: Client): def test_sdrecover_tt_nopin_nopassphrase(client: Client):
with client: with client:
device.reset(client, pin_protection=False, label="SD card") # put seed writing directly to the first backup block
mnemonic_data_bytes = prepare_data_for_sdcard()
backup_block = messages.DebugLinkSdCardDataBlock(
number=65525 + 552 + 63, data=mnemonic_data_bytes
)
client.debug.insert_sd_card(serial_number=1, data_blocks=[backup_block])
assert client.features.initialized is True IF = InputFlowBip39RecoverySdCard(client)
assert client.features.needs_backup is False client.set_input_flow(IF.get())
assert client.features.unfinished_backup is False device.recover(
assert client.features.no_backup is False client,
assert client.features.backup_type is BackupType.Bip39 pin_protection=False,
passphrase_protection=False,
label="SD recovery",
)
with client: assert client.debug.state().mnemonic_secret.decode() == MNEMONIC12
device.wipe(client)
# assert client.features.initialized is False assert client.features.pin_protection is False
# assert client.features.no_backup is True assert client.features.passphrase_protection is False
assert client.features.backup_type is messages.BackupType.Bip39
with client: assert client.features.label == "SD recovery"
device.recover(client, pin_protection=False)
state = client.debug.state()
print(f"mnemonic is {state.mnemonic_secret}")
# assert state.mnemonic_type is backup_type
# assert state.mnemonic_secret == secret

View File

@ -0,0 +1,63 @@
import pytest
from trezorlib import btc, device, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.messages import BackupType
from trezorlib.tools import parse_path
from ...common import WITH_MOCK_URANDOM
from ...input_flows import InputFlowBip39RecoverySdCard, InputFlowBip39ResetBackupSdCard
pytestmark = [pytest.mark.skip_t1, pytest.mark.skip_tr]
# NOTE: Test adapted from test_reset_recovery_bip39.py
@pytest.mark.setup_client(uninitialized=True)
@pytest.mark.sd_card(formatted=False)
def test_reset_recovery_sdcard(client: Client):
reset(client)
address_before = btc.get_address(client, "Bitcoin", parse_path("m/44h/0h/0h/0/0"))
device.wipe(client)
recover(client)
address_after = btc.get_address(client, "Bitcoin", parse_path("m/44h/0h/0h/0/0"))
assert address_before == address_after
def reset(client: Client, strength: int = 128, skip_backup: bool = False) -> None:
with WITH_MOCK_URANDOM, client:
IF = InputFlowBip39ResetBackupSdCard(client)
client.set_input_flow(IF.get())
# No PIN, no passphrase, don't display random
device.reset(
client,
display_random=False,
strength=strength,
passphrase_protection=False,
pin_protection=False,
label="test",
language="en-US",
backup_type=BackupType.Bip39,
)
# Check if device is properly initialized
assert client.features.initialized is True
assert client.features.needs_backup is False
assert client.features.pin_protection is False
assert client.features.passphrase_protection is False
def recover(client: Client):
with client:
IF = InputFlowBip39RecoverySdCard(client)
client.set_input_flow(IF.get())
client.watch_layout()
ret = device.recover(client, pin_protection=False, label="label")
# Workflow successfully ended
assert ret == messages.Success(message="Device recovered")
assert client.features.pin_protection is False
assert client.features.passphrase_protection is False

View File

@ -0,0 +1,70 @@
import itertools
import pytest
from trezorlib import btc, device, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.messages import BackupType
from trezorlib.tools import parse_path
from ...common import WITH_MOCK_URANDOM
from ...input_flows import (
InputFlowSlip39BasicRecoverySdCard,
InputFlowSlip39BasicResetRecoverySdCard,
)
# NOTE: Test adapted from test_reset_recovery_slip39_basic.py
pytestmark = [pytest.mark.skip_t1, pytest.mark.skip_tr]
sdcard_serial_numbers = [1, 2, 3, 4, 5]
@pytest.mark.setup_client(uninitialized=True)
@WITH_MOCK_URANDOM
def test_reset_recovery(client: Client):
reset(client)
address_before = btc.get_address(client, "Bitcoin", parse_path("m/44h/0h/0h/0/0"))
for selected_sdcards in itertools.combinations(sdcard_serial_numbers, 3):
device.wipe(client)
recover(client, selected_sdcards)
address_after = btc.get_address(
client, "Bitcoin", parse_path("m/44h/0h/0h/0/0")
)
assert address_before == address_after
def reset(client: Client, strength: int = 128) -> list[str]:
with client:
IF = InputFlowSlip39BasicResetRecoverySdCard(client, sdcard_serial_numbers)
client.set_input_flow(IF.get())
# No PIN, no passphrase, don't display random
device.reset(
client,
display_random=False,
strength=strength,
passphrase_protection=False,
pin_protection=False,
label="test",
language="en-US",
backup_type=BackupType.Slip39_Basic,
)
# Check if device is properly initialized
assert client.features.initialized is True
assert client.features.needs_backup is False
assert client.features.pin_protection is False
assert client.features.passphrase_protection is False
def recover(client: Client, sdcards: list[int]):
with client:
IF = InputFlowSlip39BasicRecoverySdCard(client, sdcards)
client.set_input_flow(IF.get())
ret = device.recover(client, pin_protection=False, label="label")
# Workflow successfully ended
assert ret == messages.Success(message="Device recovered")
assert client.features.pin_protection is False
assert client.features.passphrase_protection is False

View File

@ -7,12 +7,10 @@ pytestmark = [pytest.mark.skip_t1, pytest.mark.skip_tr]
@pytest.mark.sd_card(formatted=True) @pytest.mark.sd_card(formatted=True)
def test_sd_eject(client: Client): def test_sd_eject(client: Client):
print(client.features)
assert client.features.sd_card_present is True assert client.features.sd_card_present is True
client.debug.eject_sd_card() client.debug.eject_sd_card()
client.refresh_features() client.refresh_features()
print(client.features)
assert client.features.sd_card_present is False assert client.features.sd_card_present is False
client.debug.insert_sd_card(2) client.debug.insert_sd_card(2)

View File

@ -913,13 +913,14 @@ class InputFlowBip39Backup(InputFlowBase):
self.mnemonic = None self.mnemonic = None
def input_flow_common(self) -> BRGeneratorType: def input_flow_common(self) -> BRGeneratorType:
# choose Words
yield
self.debug.press_no()
# 1. Confirm Reset # 1. Confirm Reset
yield from click_through(self.debug, screens=1, code=B.ResetDevice) yield from click_through(self.debug, screens=1, code=B.ResetDevice)
# 2. Choose Words
yield
self.debug.press_no()
# mnemonic phrases and rest # mnemonic phrases and rest
self.mnemonic = yield from get_mnemonic_and_confirm_success(self.debug) self.mnemonic = yield from get_mnemonic_and_confirm_success(self.debug)
@ -935,11 +936,39 @@ class InputFlowBip39ResetBackup(InputFlowBase):
# 2. Backup your seed # 2. Backup your seed
# 3. Confirm warning # 3. Confirm warning
yield from click_through(self.debug, screens=3, code=B.ResetDevice) yield from click_through(self.debug, screens=3, code=B.ResetDevice)
# 4. Choose Words
yield
self.debug.press_no()
# mnemonic phrases and rest # mnemonic phrases and rest
self.mnemonic = yield from get_mnemonic_and_confirm_success(self.debug) self.mnemonic = yield from get_mnemonic_and_confirm_success(self.debug)
class InputFlowBip39ResetBackupSdCard(InputFlowBase):
def __init__(self, client: Client):
super().__init__(client)
def input_flow_common(self) -> BRGeneratorType:
# 1. Confirm Reset
yield from click_through(self.debug, screens=3, code=B.ResetDevice)
# 2. Choose SD Card
yield
self.debug.press_yes()
# 3. Go through format screens
yield from click_through(self.debug, screens=2, code=B.Other)
br = yield # confirm recovery seed check
assert br.code == B.Success
self.debug.press_yes()
br = yield # confirm success
assert br.code == B.Success
self.debug.press_yes()
class InputFlowBip39ResetPIN(InputFlowBase): class InputFlowBip39ResetPIN(InputFlowBase):
def __init__(self, client: Client): def __init__(self, client: Client):
super().__init__(client) super().__init__(client)
@ -1014,6 +1043,9 @@ def load_5_shares(
mnemonics: list[str] = [] mnemonics: list[str] = []
for _ in range(5): for _ in range(5):
# Choose Words
yield
debug.press_no()
# Phrase screen # Phrase screen
mnemonic = yield from read_and_confirm_mnemonic(debug) mnemonic = yield from read_and_confirm_mnemonic(debug)
assert mnemonic is not None assert mnemonic is not None
@ -1143,6 +1175,9 @@ def load_5_groups_5_shares(
for _g in range(5): for _g in range(5):
for _s in range(5): for _s in range(5):
# Choose Words
yield
debug.press_no()
# Phrase screen # Phrase screen
mnemonic = yield from read_and_confirm_mnemonic(debug) mnemonic = yield from read_and_confirm_mnemonic(debug)
assert mnemonic is not None assert mnemonic is not None
@ -1154,6 +1189,52 @@ def load_5_groups_5_shares(
return mnemonics return mnemonics
class InputFlowSlip39BasicResetRecoverySdCard(InputFlowBase):
def __init__(self, client: Client, sdcard_numbers):
super().__init__(client)
self.sdcard_numbers = sdcard_numbers
def input_flow_tt(self) -> BRGeneratorType:
# 1. Confirm Reset
# 2. Backup your seed
# 3. Confirm warning
# 4. shares info
# 5. Set & Confirm number of shares
# 6. threshold info
# 7. Set & confirm threshold value
# 8. Confirm show seeds
yield from click_through(self.debug, screens=8, code=B.ResetDevice)
# Mnemonic phrases
yield from load_5_shares_to_sdcards(self.debug, self.sdcard_numbers)
br = yield # safety warning
assert br.code == B.Success
self.debug.press_yes()
def load_5_shares_to_sdcards(debug: DebugLink, sdcard_numbers: list[int]) -> None:
for n in sdcard_numbers:
# Insert the card and erase (i.e. assume empty card)
debug.insert_sd_card(n)
debug.erase_sd_card(format=False)
# Choose SD card
yield
debug.press_yes()
# Go through format screens
yield from click_through(debug, screens=2, code=B.Other)
# Confirm continue
br = yield
assert br.code == B.Success
debug.press_yes()
# Eject the card
debug.eject_sd_card()
class InputFlowSlip39AdvancedBackup(InputFlowBase): class InputFlowSlip39AdvancedBackup(InputFlowBase):
def __init__(self, client: Client, click_info: bool): def __init__(self, client: Client, click_info: bool):
super().__init__(client) super().__init__(client)
@ -1328,11 +1409,29 @@ class InputFlowBip39Recovery(InputFlowBase):
yield from self.REC.confirm_recovery() yield from self.REC.confirm_recovery()
if self.pin is not None: if self.pin is not None:
yield from self.PIN.setup_new_pin(self.pin) yield from self.PIN.setup_new_pin(self.pin)
# Choose Words
yield
self.debug.press_no()
yield from self.REC.setup_bip39_recovery(len(self.mnemonic)) yield from self.REC.setup_bip39_recovery(len(self.mnemonic))
yield from self.REC.input_mnemonic(self.mnemonic) yield from self.REC.input_mnemonic(self.mnemonic)
yield from self.REC.success_wallet_recovered() yield from self.REC.success_wallet_recovered()
class InputFlowBip39RecoverySdCard(InputFlowBase):
def __init__(self, client: Client, pin: str | None = None):
super().__init__(client)
self.pin = pin
def input_flow_common(self) -> BRGeneratorType:
yield from self.REC.confirm_recovery()
if self.pin is not None:
yield from self.PIN.setup_new_pin(self.pin)
# Choose "SD card"
yield
self.debug.press_yes()
yield from self.REC.success_wallet_recovered()
class InputFlowSlip39AdvancedRecoveryDryRun(InputFlowBase): class InputFlowSlip39AdvancedRecoveryDryRun(InputFlowBase):
def __init__(self, client: Client, shares: list[str], mismatch: bool = False): def __init__(self, client: Client, shares: list[str], mismatch: bool = False):
super().__init__(client) super().__init__(client)
@ -1478,6 +1577,35 @@ class InputFlowSlip39BasicRecovery(InputFlowBase):
yield from self.REC.success_wallet_recovered() yield from self.REC.success_wallet_recovered()
class InputFlowSlip39BasicRecoverySdCard(InputFlowBase):
def __init__(self, client: Client, sdcard_numbers: list[int], pin: str | None = None):
super().__init__(client)
self.sdcard_numbers = sdcard_numbers
self.pin = pin
def input_flow_common(self) -> BRGeneratorType:
yield from self.REC.confirm_recovery()
if self.pin is not None:
yield from self.PIN.setup_new_pin(self.pin)
# "Words" counterpart:
# yield from self.REC.setup_slip39_recovery(self.word_count)
# yield from self.REC.input_all_slip39_shares(self.shares)
# choose SD card
for n in self.sdcard_numbers:
self.debug.eject_sd_card()
self.debug.insert_sd_card(n)
# choose SD card
yield
self.debug.press_yes()
# enter next share
yield
self.debug.press_yes()
yield from self.REC.success_wallet_recovered()
class InputFlowSlip39BasicRecoveryAbort(InputFlowBase): class InputFlowSlip39BasicRecoveryAbort(InputFlowBase):
def __init__(self, client: Client): def __init__(self, client: Client):
super().__init__(client) super().__init__(client)