1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-04-20 17:19:01 +00:00
This commit is contained in:
Martin Milata 2025-04-08 15:33:16 +02:00
parent 04aeaf0607
commit 430023e099
20 changed files with 562 additions and 58 deletions

View File

@ -1,4 +1,4 @@
check: messages.pb messages-binance.pb messages-bitcoin.pb messages-bootloader.pb messages-cardano.pb messages-common.pb messages-crypto.pb messages-debug.pb messages-ethereum.pb messages-management.pb messages-monero.pb messages-nem.pb messages-ripple.pb messages-stellar.pb messages-tezos.pb messages-eos.pb
check: messages.pb messages-binance.pb messages-bitcoin.pb messages-ble.pb messages-bootloader.pb messages-cardano.pb messages-common.pb messages-crypto.pb messages-debug.pb messages-ethereum.pb messages-management.pb messages-monero.pb messages-nem.pb messages-ripple.pb messages-stellar.pb messages-tezos.pb messages-eos.pb
%.pb: %.proto
protoc -I/usr/include -I. $< -o $@

View File

@ -20,3 +20,11 @@ message BleUnpair {
optional bool all = 1; // whether to erase bonds for all devices
}
/**
* Request: disconnect
* @start
* @next Success
* @next Failure
*/
message BleDisconnect {
}

View File

@ -39,6 +39,7 @@ message Failure {
Failure_PinMismatch = 12;
Failure_WipeCodeMismatch = 13;
Failure_InvalidSession = 14;
Failure_DeviceIsBusy = 15; // FIXME: probably remove
Failure_FirmwareError = 99;
}
}

View File

@ -16,4 +16,5 @@ NotInitialized = 11
PinMismatch = 12
WipeCodeMismatch = 13
InvalidSession = 14
DeviceIsBusy = 15
FirmwareError = 99

View File

@ -39,6 +39,7 @@ if TYPE_CHECKING:
PinMismatch = 12
WipeCodeMismatch = 13
InvalidSession = 14
DeviceIsBusy = 15
FirmwareError = 99
class ButtonRequestType(IntEnum):

View File

@ -1270,6 +1270,12 @@ if TYPE_CHECKING:
def is_type_of(cls, msg: Any) -> TypeGuard["BleUnpair"]:
return isinstance(msg, cls)
class BleDisconnect(protobuf.MessageType):
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["BleDisconnect"]:
return isinstance(msg, cls)
class CardanoBlockchainPointerType(protobuf.MessageType):
block_index: "int"
tx_index: "int"

View File

@ -7,7 +7,7 @@ package-mode = false
[tool.poetry.dependencies]
# all
python = "^3.9"
python = "<3.14,>=3.9"
trezor = {path = "./python", develop = true}
tvl = {path = "./vendor/ts-tvl", develop = true}
scons = "*"
@ -80,6 +80,10 @@ flake8-annotations = "^3.1.1"
pyelftools = "^0.32"
pytest-retry = "^1.7.0"
# ble
#dbus-fast = "*"
bleak = "^0.22.3"
[tool.poetry.dev-dependencies]
scan-build = "*"
towncrier = "^23.6.0"

View File

@ -9,3 +9,4 @@ construct>=2.9,!=2.10.55
typing_extensions>=4.7.1
construct-classes>=0.1.2
cryptography>=41
bleak>=0.22

View File

@ -29,7 +29,7 @@ per-file-ignores =
helper-scripts/*:I
tools/*:I
tests/*:I
known-modules = libusb1:[usb1],hidapi:[hid],PyQt5:[PyQt5.QtWidgets,PyQt5.QtGui,PyQt5.QtCore]
known-modules = libusb1:[usb1],hidapi:[hid],PyQt5:[PyQt5.QtWidgets,PyQt5.QtGui,PyQt5.QtCore],dbus-next:[dbus_next]
[isort]
profile = black

View File

@ -19,3 +19,15 @@ def unpair(
return
else:
raise RuntimeError(f"Unexpected message {resp}")
@session
def disconnect(
client: "TrezorClient",
):
resp = client.call(messages.BleDisconnect())
if isinstance(resp, messages.Success):
return
else:
raise RuntimeError(f"Unexpected message {resp}")

View File

@ -21,7 +21,7 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, Optional
import click
from .. import exceptions, transport
from .. import exceptions, messages, transport
from ..client import TrezorClient
from ..ui import ClickUI, ScriptUI
@ -110,6 +110,12 @@ class TrezorConnection:
except transport.DeviceIsBusy:
click.echo("Device is in use by another process.")
sys.exit(1)
except exceptions.TrezorFailure as e:
if e.code is messages.FailureType.DeviceIsBusy:
click.echo(str(e))
sys.exit(1)
else:
raise e
except Exception:
click.echo("Failed to find a Trezor device.")
if self.path is not None:

View File

@ -15,16 +15,14 @@
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
import sys
from typing import TYPE_CHECKING
import click
from .. import ble, exceptions
from ..client import TrezorClient
from ..transport.ble import BleProxy
from . import with_client
if TYPE_CHECKING:
from ..client import TrezorClient
@click.group(name="ble")
def cli() -> None:
@ -53,3 +51,54 @@ def unpair(
except exceptions.TrezorException as e:
click.echo(f"Unpair failed: {e}")
sys.exit(3)
@cli.command()
def connect() -> None:
"""Connect to the device via BLE."""
ble = BleProxy()
click.echo("Scanning...")
devices = ble.scan()
if len(devices) == 0:
click.echo("No BLE devices found")
return
else:
click.echo(f"Found {len(devices)} BLE device(s)")
for address, name in devices:
click.echo(f"Device: {name}, {address}")
device = devices[0]
click.echo(f"Connecting to {device[1]}...")
ble.connect(device[0])
click.echo("Connected")
@with_client
def disconnect_device(client: "TrezorClient") -> None:
"""Disconnect from device side."""
try:
ble.disconnect(client)
except exceptions.Cancelled:
click.echo("Disconnect aborted on device.")
except exceptions.TrezorException as e:
click.echo(f"Disconnect failed: {e}")
sys.exit(3)
@cli.command()
@click.option("--device", is_flag=True, help="Disconnect from device side.")
def disconnect(device: bool) -> None:
if device:
disconnect_device()
else:
ble_proxy = BleProxy()
devices = [d for d in ble_proxy.lookup() if d.connected]
if len(devices) == 0:
click.echo("No BLE devices found")
return
ble_proxy.connect(devices[0].address)
ble_proxy.disconnect()

View File

@ -296,8 +296,8 @@ def list_devices(no_resolve: bool) -> Optional[Iterable["Transport"]]:
client.end_session()
except DeviceIsBusy:
description = "Device is in use by another process"
except Exception:
description = "Failed to read details"
# except Exception:
# description = "Failed to read details"
click.echo(f"{transport} - {description}")
return None

View File

@ -43,6 +43,7 @@ class FailureType(IntEnum):
PinMismatch = 12
WipeCodeMismatch = 13
InvalidSession = 14
DeviceIsBusy = 15
FirmwareError = 99
@ -2222,6 +2223,10 @@ class BleUnpair(protobuf.MessageType):
self.all = all
class BleDisconnect(protobuf.MessageType):
MESSAGE_WIRE_TYPE = None
class FirmwareErase(protobuf.MessageType):
MESSAGE_WIRE_TYPE = 6
FIELDS = {

View File

@ -110,6 +110,7 @@ class Transport:
def all_transports() -> Iterable[type["Transport"]]:
from .ble import BleTransport
from .bridge import BridgeTransport
from .hid import HidTransport
from .udp import UdpTransport
@ -120,7 +121,9 @@ def all_transports() -> Iterable[type["Transport"]]:
HidTransport,
UdpTransport,
WebUsbTransport,
BleTransport,
)
print(f"transports {transports}")
return set(t for t in transports if t.ENABLED)
@ -161,6 +164,7 @@ def get_transport(path: str | None = None, prefix_search: bool = False) -> Trans
)
)
transports = [t for t in all_transports() if match_prefix(path, t.PATH_PREFIX)]
print(f"transports {transports}")
if transports:
return transports[0].find_by_path(path, prefix_search=prefix_search)

View File

@ -0,0 +1,291 @@
# This file is part of the Trezor project.
#
# Copyright (C) 2012-2025 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 asyncio
import atexit
import logging
from dataclasses import dataclass
from multiprocessing import Pipe, Process
from multiprocessing.connection import Connection
from typing import TYPE_CHECKING, Any, Iterable, Optional
from . import Timeout, TransportException
from .protocol import ProtocolBasedTransport, ProtocolV1
if TYPE_CHECKING:
from ..models import TrezorModel
LOG = logging.getLogger(__name__)
TREZOR_SERVICE_UUID = "8c000001-a59b-4d58-a9ad-073df69fa1b1"
TREZOR_CHARACTERISTIC_RX = "8c000002-a59b-4d58-a9ad-073df69fa1b1"
TREZOR_CHARACTERISTIC_TX = "8c000003-a59b-4d58-a9ad-073df69fa1b1"
class BleTransport(ProtocolBasedTransport):
ENABLED = True
PATH_PREFIX = "ble"
_ble = None
def __init__(self, mac_addr: str) -> None:
self.device = mac_addr
super().__init__(protocol=ProtocolV1(self, replen=244))
def get_path(self) -> str:
return "{}:{}".format(self.PATH_PREFIX, self.device)
def find_debug(self) -> "BleTransport":
return BleTransport(self.device) # FIXME WebUsb
@classmethod
def enumerate(
cls, _models: Optional[Iterable["TrezorModel"]] = None # FIXME models
) -> Iterable["BleTransport"]:
devices = cls.ble().scan()
# FIXME we're dropping the name here
return [BleTransport(device[0]) for device in devices]
@classmethod
def _try_path(cls, path: str) -> "BleTransport":
devices = cls.enumerate(None)
devices = [d for d in devices if d.device == path]
if len(devices) == 0:
raise TransportException(f"No BLE device: {path}")
return devices[0]
@classmethod
def find_by_path(cls, path: str, prefix_search: bool = False) -> "BleTransport":
if not prefix_search:
raise TransportException
if prefix_search:
return super().find_by_path(path, prefix_search)
else:
raise TransportException(f"No BLE device: {path}")
def open(self) -> None:
self.ble().connect(self.device)
def close(self) -> None:
pass # self.ble().disconnect()
def write_chunk(self, chunk: bytes) -> None:
self.ble().write(self.device, chunk)
def read_chunk(self, timeout: Optional[float] = None) -> bytes:
chunk = self.ble().read(self.device, timeout)
# LOG.log(DUMP_PACKETS, f"received packet: {chunk.hex()}")
if len(chunk) != 64:
raise TransportException(f"Unexpected chunk size: {len(chunk)}")
return bytearray(chunk)
@classmethod
def ble(cls) -> "BleProxy":
if cls._ble is None:
cls._ble = BleProxy()
return cls._ble
class BleProxy:
pipe = None
process = None
def __init__(self):
if self.pipe is not None:
return
parent_pipe, child_pipe = Pipe()
self.pipe = parent_pipe
self.process = Process(target=BleAsync, args=(child_pipe,), daemon=True)
self.process.start()
atexit.register(self._shutdown)
def __getattr__(self, name: str):
def f(*args: Any, **kwargs: Any):
assert self.pipe is not None
self.pipe.send((name, args, kwargs))
result = self.pipe.recv()
if isinstance(result, BaseException):
raise result
return result
return f
def _shutdown(self):
self.pipe.send(("shutdown", [], {}))
self.process.join(10) # is timeout
class BleAsync:
class Shutdown(Exception):
pass
# @dataclass
# class Peripheral:
# device = None
# adv_data = None
# client = None
# queue = None
def __init__(self, pipe: Connection):
asyncio.run(self.main(pipe))
async def main(self, pipe: Connection):
self.connected = {}
# TODO: currently only one concurrent device is supported
self.current = None
self.devices = {}
self.queue = asyncio.Queue()
self.scanned = None
LOG.debug("async BLE process started")
# TODO: signal ready to main process?
while True:
await ready(pipe)
cmd, args, kwargs = pipe.recv()
try:
result = await getattr(self, cmd)(*args, **kwargs)
except self.Shutdown:
LOG.debug("async BLE exit loop")
break
except Timeout as e:
await ready(pipe, write=True)
pipe.send(e)
except Exception as e:
LOG.exception("Error in async BLE process:")
await ready(pipe, write=True)
pipe.send(e)
break
else:
await ready(pipe, write=True)
pipe.send(result)
await self.disconnect("FIXME") # TODO foreach
async def scan(self) -> list[tuple[str, str]]:
LOG.debug("scanning BLE")
from bleak import BleakScanner
# NOTE BleakScanner.discover(service_uuids=[TREZOR_SERVICE_UUID]) is broken
# problem possibly on the bluez side
devices = await BleakScanner.discover(
timeout=3,
return_adv=True,
)
self.scanned = []
res = []
for address, (dev, adv_data) in devices.items():
if TREZOR_SERVICE_UUID not in adv_data.service_uuids:
continue
LOG.debug(f"scan: {dev.address}: {dev.name} rssi={adv_data.rssi} manufacturer_data={adv_data.manufacturer_data}")
self.scanned.append(dev)
res.append((dev.address, dev.name)) # FIXME
return res
async def connect(self, address: str):
from bleak import BleakClient
# already connected?
# scanned?
# connect by addr
if self.current and self.current.address == address:
return
ble_device = self.devices.get(address)
if ble_device:
LOG.debug(f"Already connected to {ble_device.address}")
self.current = ble_device
return
if self.scanned is None:
await self.scan()
for dev in self.scanned:
if dev.address == address:
break
else:
raise RuntimeError("device not found")
LOG.debug(f"Connecting to {address}...")
ble_device = BleakClient(dev) # TODO: services, timeout
await ble_device.connect()
self.current = ble_device
# import subprocess
# subprocess.run("gnome-control-center bluetooth")
await ble_device.pair()
self.devices[address] = ble_device
async def read_callback(characteristic, data):
await self.queue.put(data)
await ble_device.start_notify(TREZOR_CHARACTERISTIC_TX, read_callback)
LOG.info(f"Connected to {ble_device.address}")
async def disconnect(self, address: str):
if self.current is None:
return
ble_device = self.current
await ble_device.stop_notify(TREZOR_CHARACTERISTIC_TX)
await ble_device.disconnect() # throws EOFError sometimes
LOG.info(f"Disconnected from {ble_device.address}")
self.current = None
async def read(self, address: str, timeout: float | None):
assert self.current
try:
return await asyncio.wait_for(self.queue.get(), timeout=timeout)
except (TimeoutError, asyncio.TimeoutError):
raise Timeout(f"Timeout reading BLE packet ({timeout}s)")
async def write(self, address: str, chunk: bytes):
assert self.current
await self.current.write_gatt_char(TREZOR_CHARACTERISTIC_RX, chunk, response=False)
async def shutdown(self):
raise self.Shutdown
async def ready(f: Any, write: bool = False):
"""Asynchronously wait for file-like object to become ready for reading or writing."""
fd = f.fileno()
loop = asyncio.get_event_loop()
event = asyncio.Event()
if write:
def callback():
event.set()
loop.remove_writer(fd)
loop.add_writer(fd, callback)
else:
def callback():
event.set()
loop.remove_reader(fd)
loop.add_reader(fd, callback)
await event.wait()

View File

@ -78,8 +78,9 @@ class Protocol:
its messages.
"""
def __init__(self, handle: Handle) -> None:
def __init__(self, handle: Handle, replen: int = REPLEN) -> None:
self.handle = handle
self.replen = replen
self.session_counter = 0
# XXX we might be able to remove this now that TrezorClient does session handling
@ -144,10 +145,10 @@ class ProtocolV1(Protocol):
while buffer:
# Report ID, data padded to 63 bytes
chunk = b"?" + buffer[: REPLEN - 1]
chunk = chunk.ljust(REPLEN, b"\x00")
chunk = b"?" + buffer[: self.replen - 1]
chunk = chunk.ljust(self.replen, b"\x00")
self.handle.write_chunk(chunk)
buffer = buffer[63:]
buffer = buffer[self.replen - 1 :]
def read(self, timeout: float | None = None) -> MessagePayload:
if timeout is None:

View File

@ -68,6 +68,8 @@ class WebUsbHandle:
self.handle.claimInterface(self.interface)
except usb1.USBErrorAccess as e:
raise DeviceIsBusy(self.device) from e
except usb1.USBErrorBusy as e:
raise DeviceIsBusy(self.device) from e
def close(self) -> None:
if self.handle is not None:

View File

@ -166,10 +166,114 @@ impl ::protobuf::reflect::ProtobufValue for BleUnpair {
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
}
// @@protoc_insertion_point(message:hw.trezor.messages.ble.BleDisconnect)
#[derive(PartialEq,Clone,Default,Debug)]
pub struct BleDisconnect {
// special fields
// @@protoc_insertion_point(special_field:hw.trezor.messages.ble.BleDisconnect.special_fields)
pub special_fields: ::protobuf::SpecialFields,
}
impl<'a> ::std::default::Default for &'a BleDisconnect {
fn default() -> &'a BleDisconnect {
<BleDisconnect as ::protobuf::Message>::default_instance()
}
}
impl BleDisconnect {
pub fn new() -> BleDisconnect {
::std::default::Default::default()
}
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
let mut fields = ::std::vec::Vec::with_capacity(0);
let mut oneofs = ::std::vec::Vec::with_capacity(0);
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<BleDisconnect>(
"BleDisconnect",
fields,
oneofs,
)
}
}
impl ::protobuf::Message for BleDisconnect {
const NAME: &'static str = "BleDisconnect";
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
while let Some(tag) = is.read_raw_tag_or_eof()? {
match tag {
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;
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<()> {
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() -> BleDisconnect {
BleDisconnect::new()
}
fn clear(&mut self) {
self.special_fields.clear();
}
fn default_instance() -> &'static BleDisconnect {
static instance: BleDisconnect = BleDisconnect {
special_fields: ::protobuf::SpecialFields::new(),
};
&instance
}
}
impl ::protobuf::MessageFull for BleDisconnect {
fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
descriptor.get(|| file_descriptor().message_by_package_relative_name("BleDisconnect").unwrap()).clone()
}
}
impl ::std::fmt::Display for BleDisconnect {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for BleDisconnect {
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x12messages-ble.proto\x12\x16hw.trezor.messages.ble\x1a\roptions.prot\
o\"\x1d\n\tBleUnpair\x12\x10\n\x03all\x18\x01\x20\x01(\x08R\x03allB;\n#c\
om.satoshilabs.trezor.lib.protobufB\x10TrezorMessageBle\x80\xa6\x1d\x01\
o\"\x1d\n\tBleUnpair\x12\x10\n\x03all\x18\x01\x20\x01(\x08R\x03all\"\x0f\
\n\rBleDisconnectB;\n#com.satoshilabs.trezor.lib.protobufB\x10TrezorMess\
ageBle\x80\xa6\x1d\x01\
";
/// `FileDescriptorProto` object which was a source for this generated file
@ -188,8 +292,9 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor {
let generated_file_descriptor = generated_file_descriptor_lazy.get(|| {
let mut deps = ::std::vec::Vec::with_capacity(1);
deps.push(super::options::file_descriptor().clone());
let mut messages = ::std::vec::Vec::with_capacity(1);
let mut messages = ::std::vec::Vec::with_capacity(2);
messages.push(BleUnpair::generated_message_descriptor_data());
messages.push(BleDisconnect::generated_message_descriptor_data());
let mut enums = ::std::vec::Vec::with_capacity(0);
::protobuf::reflect::GeneratedFileDescriptor::new_generated(
file_descriptor_proto(),

View File

@ -414,6 +414,8 @@ pub mod failure {
Failure_WipeCodeMismatch = 13,
// @@protoc_insertion_point(enum_value:hw.trezor.messages.common.Failure.FailureType.Failure_InvalidSession)
Failure_InvalidSession = 14,
// @@protoc_insertion_point(enum_value:hw.trezor.messages.common.Failure.FailureType.Failure_DeviceIsBusy)
Failure_DeviceIsBusy = 15,
// @@protoc_insertion_point(enum_value:hw.trezor.messages.common.Failure.FailureType.Failure_FirmwareError)
Failure_FirmwareError = 99,
}
@ -441,6 +443,7 @@ pub mod failure {
12 => ::std::option::Option::Some(FailureType::Failure_PinMismatch),
13 => ::std::option::Option::Some(FailureType::Failure_WipeCodeMismatch),
14 => ::std::option::Option::Some(FailureType::Failure_InvalidSession),
15 => ::std::option::Option::Some(FailureType::Failure_DeviceIsBusy),
99 => ::std::option::Option::Some(FailureType::Failure_FirmwareError),
_ => ::std::option::Option::None
}
@ -462,6 +465,7 @@ pub mod failure {
"Failure_PinMismatch" => ::std::option::Option::Some(FailureType::Failure_PinMismatch),
"Failure_WipeCodeMismatch" => ::std::option::Option::Some(FailureType::Failure_WipeCodeMismatch),
"Failure_InvalidSession" => ::std::option::Option::Some(FailureType::Failure_InvalidSession),
"Failure_DeviceIsBusy" => ::std::option::Option::Some(FailureType::Failure_DeviceIsBusy),
"Failure_FirmwareError" => ::std::option::Option::Some(FailureType::Failure_FirmwareError),
_ => ::std::option::Option::None
}
@ -482,6 +486,7 @@ pub mod failure {
FailureType::Failure_PinMismatch,
FailureType::Failure_WipeCodeMismatch,
FailureType::Failure_InvalidSession,
FailureType::Failure_DeviceIsBusy,
FailureType::Failure_FirmwareError,
];
}
@ -508,7 +513,8 @@ pub mod failure {
FailureType::Failure_PinMismatch => 11,
FailureType::Failure_WipeCodeMismatch => 12,
FailureType::Failure_InvalidSession => 13,
FailureType::Failure_FirmwareError => 14,
FailureType::Failure_DeviceIsBusy => 14,
FailureType::Failure_FirmwareError => 15,
};
Self::enum_descriptor().value_by_index(index)
}
@ -2481,9 +2487,9 @@ impl ::protobuf::reflect::ProtobufValue for HDNodeType {
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x15messages-common.proto\x12\x19hw.trezor.messages.common\x1a\roption\
s.proto\"%\n\x07Success\x12\x1a\n\x07message\x18\x01\x20\x01(\t:\0R\x07m\
essage\"\x8f\x04\n\x07Failure\x12B\n\x04code\x18\x01\x20\x01(\x0e2..hw.t\
essage\"\xa9\x04\n\x07Failure\x12B\n\x04code\x18\x01\x20\x01(\x0e2..hw.t\
rezor.messages.common.Failure.FailureTypeR\x04code\x12\x18\n\x07message\
\x18\x02\x20\x01(\tR\x07message\"\xa5\x03\n\x0bFailureType\x12\x1d\n\x19\
\x18\x02\x20\x01(\tR\x07message\"\xbf\x03\n\x0bFailureType\x12\x1d\n\x19\
Failure_UnexpectedMessage\x10\x01\x12\x1a\n\x16Failure_ButtonExpected\
\x10\x02\x12\x15\n\x11Failure_DataError\x10\x03\x12\x1b\n\x17Failure_Act\
ionCancelled\x10\x04\x12\x17\n\x13Failure_PinExpected\x10\x05\x12\x18\n\
@ -2492,44 +2498,45 @@ static file_descriptor_proto_data: &'static [u8] = b"\
essError\x10\t\x12\x1a\n\x16Failure_NotEnoughFunds\x10\n\x12\x1a\n\x16Fa\
ilure_NotInitialized\x10\x0b\x12\x17\n\x13Failure_PinMismatch\x10\x0c\
\x12\x1c\n\x18Failure_WipeCodeMismatch\x10\r\x12\x1a\n\x16Failure_Invali\
dSession\x10\x0e\x12\x19\n\x15Failure_FirmwareError\x10c\"\xab\x06\n\rBu\
ttonRequest\x12N\n\x04code\x18\x01\x20\x01(\x0e2:.hw.trezor.messages.com\
mon.ButtonRequest.ButtonRequestTypeR\x04code\x12\x14\n\x05pages\x18\x02\
\x20\x01(\rR\x05pages\x12\x12\n\x04name\x18\x04\x20\x01(\tR\x04name\"\
\x99\x05\n\x11ButtonRequestType\x12\x17\n\x13ButtonRequest_Other\x10\x01\
\x12\"\n\x1eButtonRequest_FeeOverThreshold\x10\x02\x12\x1f\n\x1bButtonRe\
quest_ConfirmOutput\x10\x03\x12\x1d\n\x19ButtonRequest_ResetDevice\x10\
\x04\x12\x1d\n\x19ButtonRequest_ConfirmWord\x10\x05\x12\x1c\n\x18ButtonR\
equest_WipeDevice\x10\x06\x12\x1d\n\x19ButtonRequest_ProtectCall\x10\x07\
\x12\x18\n\x14ButtonRequest_SignTx\x10\x08\x12\x1f\n\x1bButtonRequest_Fi\
rmwareCheck\x10\t\x12\x19\n\x15ButtonRequest_Address\x10\n\x12\x1b\n\x17\
ButtonRequest_PublicKey\x10\x0b\x12#\n\x1fButtonRequest_MnemonicWordCoun\
t\x10\x0c\x12\x1f\n\x1bButtonRequest_MnemonicInput\x10\r\x120\n(_Depreca\
ted_ButtonRequest_PassphraseType\x10\x0e\x1a\x02\x08\x01\x12'\n#ButtonRe\
quest_UnknownDerivationPath\x10\x0f\x12\"\n\x1eButtonRequest_RecoveryHom\
epage\x10\x10\x12\x19\n\x15ButtonRequest_Success\x10\x11\x12\x19\n\x15Bu\
ttonRequest_Warning\x10\x12\x12!\n\x1dButtonRequest_PassphraseEntry\x10\
\x13\x12\x1a\n\x16ButtonRequest_PinEntry\x10\x14J\x04\x08\x03\x10\x04\"\
\x0b\n\tButtonAck\"\xbb\x02\n\x10PinMatrixRequest\x12T\n\x04type\x18\x01\
\x20\x01(\x0e2@.hw.trezor.messages.common.PinMatrixRequest.PinMatrixRequ\
estTypeR\x04type\"\xd0\x01\n\x14PinMatrixRequestType\x12\x20\n\x1cPinMat\
rixRequestType_Current\x10\x01\x12!\n\x1dPinMatrixRequestType_NewFirst\
\x10\x02\x12\"\n\x1ePinMatrixRequestType_NewSecond\x10\x03\x12&\n\"PinMa\
trixRequestType_WipeCodeFirst\x10\x04\x12'\n#PinMatrixRequestType_WipeCo\
deSecond\x10\x05\"\x20\n\x0cPinMatrixAck\x12\x10\n\x03pin\x18\x01\x20\
\x02(\tR\x03pin\"5\n\x11PassphraseRequest\x12\x20\n\n_on_device\x18\x01\
\x20\x01(\x08R\x08OnDeviceB\x02\x18\x01\"g\n\rPassphraseAck\x12\x1e\n\np\
assphrase\x18\x01\x20\x01(\tR\npassphrase\x12\x19\n\x06_state\x18\x02\
\x20\x01(\x0cR\x05StateB\x02\x18\x01\x12\x1b\n\ton_device\x18\x03\x20\
\x01(\x08R\x08onDevice\"=\n!Deprecated_PassphraseStateRequest\x12\x14\n\
\x05state\x18\x01\x20\x01(\x0cR\x05state:\x02\x18\x01\"#\n\x1dDeprecated\
_PassphraseStateAck:\x02\x18\x01\"\xc0\x01\n\nHDNodeType\x12\x14\n\x05de\
pth\x18\x01\x20\x02(\rR\x05depth\x12\x20\n\x0bfingerprint\x18\x02\x20\
\x02(\rR\x0bfingerprint\x12\x1b\n\tchild_num\x18\x03\x20\x02(\rR\x08chil\
dNum\x12\x1d\n\nchain_code\x18\x04\x20\x02(\x0cR\tchainCode\x12\x1f\n\
\x0bprivate_key\x18\x05\x20\x01(\x0cR\nprivateKey\x12\x1d\n\npublic_key\
\x18\x06\x20\x02(\x0cR\tpublicKeyB>\n#com.satoshilabs.trezor.lib.protobu\
fB\x13TrezorMessageCommon\x80\xa6\x1d\x01\
dSession\x10\x0e\x12\x18\n\x14Failure_DeviceIsBusy\x10\x0f\x12\x19\n\x15\
Failure_FirmwareError\x10c\"\xab\x06\n\rButtonRequest\x12N\n\x04code\x18\
\x01\x20\x01(\x0e2:.hw.trezor.messages.common.ButtonRequest.ButtonReques\
tTypeR\x04code\x12\x14\n\x05pages\x18\x02\x20\x01(\rR\x05pages\x12\x12\n\
\x04name\x18\x04\x20\x01(\tR\x04name\"\x99\x05\n\x11ButtonRequestType\
\x12\x17\n\x13ButtonRequest_Other\x10\x01\x12\"\n\x1eButtonRequest_FeeOv\
erThreshold\x10\x02\x12\x1f\n\x1bButtonRequest_ConfirmOutput\x10\x03\x12\
\x1d\n\x19ButtonRequest_ResetDevice\x10\x04\x12\x1d\n\x19ButtonRequest_C\
onfirmWord\x10\x05\x12\x1c\n\x18ButtonRequest_WipeDevice\x10\x06\x12\x1d\
\n\x19ButtonRequest_ProtectCall\x10\x07\x12\x18\n\x14ButtonRequest_SignT\
x\x10\x08\x12\x1f\n\x1bButtonRequest_FirmwareCheck\x10\t\x12\x19\n\x15Bu\
ttonRequest_Address\x10\n\x12\x1b\n\x17ButtonRequest_PublicKey\x10\x0b\
\x12#\n\x1fButtonRequest_MnemonicWordCount\x10\x0c\x12\x1f\n\x1bButtonRe\
quest_MnemonicInput\x10\r\x120\n(_Deprecated_ButtonRequest_PassphraseTyp\
e\x10\x0e\x1a\x02\x08\x01\x12'\n#ButtonRequest_UnknownDerivationPath\x10\
\x0f\x12\"\n\x1eButtonRequest_RecoveryHomepage\x10\x10\x12\x19\n\x15Butt\
onRequest_Success\x10\x11\x12\x19\n\x15ButtonRequest_Warning\x10\x12\x12\
!\n\x1dButtonRequest_PassphraseEntry\x10\x13\x12\x1a\n\x16ButtonRequest_\
PinEntry\x10\x14J\x04\x08\x03\x10\x04\"\x0b\n\tButtonAck\"\xbb\x02\n\x10\
PinMatrixRequest\x12T\n\x04type\x18\x01\x20\x01(\x0e2@.hw.trezor.message\
s.common.PinMatrixRequest.PinMatrixRequestTypeR\x04type\"\xd0\x01\n\x14P\
inMatrixRequestType\x12\x20\n\x1cPinMatrixRequestType_Current\x10\x01\
\x12!\n\x1dPinMatrixRequestType_NewFirst\x10\x02\x12\"\n\x1ePinMatrixReq\
uestType_NewSecond\x10\x03\x12&\n\"PinMatrixRequestType_WipeCodeFirst\
\x10\x04\x12'\n#PinMatrixRequestType_WipeCodeSecond\x10\x05\"\x20\n\x0cP\
inMatrixAck\x12\x10\n\x03pin\x18\x01\x20\x02(\tR\x03pin\"5\n\x11Passphra\
seRequest\x12\x20\n\n_on_device\x18\x01\x20\x01(\x08R\x08OnDeviceB\x02\
\x18\x01\"g\n\rPassphraseAck\x12\x1e\n\npassphrase\x18\x01\x20\x01(\tR\n\
passphrase\x12\x19\n\x06_state\x18\x02\x20\x01(\x0cR\x05StateB\x02\x18\
\x01\x12\x1b\n\ton_device\x18\x03\x20\x01(\x08R\x08onDevice\"=\n!Depreca\
ted_PassphraseStateRequest\x12\x14\n\x05state\x18\x01\x20\x01(\x0cR\x05s\
tate:\x02\x18\x01\"#\n\x1dDeprecated_PassphraseStateAck:\x02\x18\x01\"\
\xc0\x01\n\nHDNodeType\x12\x14\n\x05depth\x18\x01\x20\x02(\rR\x05depth\
\x12\x20\n\x0bfingerprint\x18\x02\x20\x02(\rR\x0bfingerprint\x12\x1b\n\t\
child_num\x18\x03\x20\x02(\rR\x08childNum\x12\x1d\n\nchain_code\x18\x04\
\x20\x02(\x0cR\tchainCode\x12\x1f\n\x0bprivate_key\x18\x05\x20\x01(\x0cR\
\nprivateKey\x12\x1d\n\npublic_key\x18\x06\x20\x02(\x0cR\tpublicKeyB>\n#\
com.satoshilabs.trezor.lib.protobufB\x13TrezorMessageCommon\x80\xa6\x1d\
\x01\
";
/// `FileDescriptorProto` object which was a source for this generated file