31 #include <boost/algorithm/string/classification.hpp> 32 #include <boost/algorithm/string/split.hpp> 33 #include <boost/regex.hpp> 38 #ifdef WITH_DEVICE_TREZOR 40 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY 41 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "device.trezor" 42 #define TREZOR_BIP44_HARDENED_ZERO 0x80000000 44 const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080};
46 device_trezor_base::device_trezor_base(): m_callback(nullptr), m_last_msg_type(messages::MessageType_Success) {
47 #ifdef WITH_TREZOR_DEBUGGING 52 device_trezor_base::~device_trezor_base() {
56 }
catch(std::exception
const& e){
57 MERROR(
"Could not disconnect and release: " << e.what());
65 bool device_trezor_base::reset() {
70 this->m_full_name =
name;
73 auto delim =
name.find(
':');
74 if (delim != std::string::npos && delim + 1 <
name.length()) {
75 this->name =
name.substr(delim + 1);
81 const std::string device_trezor_base::get_name()
const {
82 if (this->m_full_name.empty()) {
83 return std::string(
"<disconnected:").append(this->name).append(
">");
85 return this->m_full_name;
97 bool device_trezor_base::release() {
102 }
catch(std::exception
const& e){
103 MERROR(
"Release exception: " << e.what());
108 bool device_trezor_base::connect() {
116 MDEBUG(
"Enumerating Trezor devices...");
120 MDEBUG(
"Enumeration yielded " << trans.size() <<
" Trezor devices");
121 for (
auto &cur : trans) {
122 MDEBUG(
" device: " << *(cur.get()));
125 for (
auto &cur : trans) {
127 if (boost::starts_with(cur_path, this->name)) {
128 MDEBUG(
"Device Match: " << cur_path);
135 MERROR(
"No matching Trezor device found. Device specifier: \"" + this->name +
"\"");
141 #ifdef WITH_TREZOR_DEBUGGING 146 }
catch(std::exception
const& e){
147 MERROR(
"Open exception: " << e.what());
152 bool device_trezor_base::disconnect() {
154 m_device_state.clear();
159 m_transport->close();
160 m_transport =
nullptr;
162 }
catch(std::exception
const& e){
163 MERROR(
"Disconnect exception: " << e.what());
164 m_transport =
nullptr;
169 #ifdef WITH_TREZOR_DEBUGGING 170 if (m_debug_callback) {
171 m_debug_callback->on_disconnect();
172 m_debug_callback =
nullptr;
183 void device_trezor_base::lock() {
184 MTRACE(
"Ask for LOCKING for device " << this->name <<
" in thread ");
185 device_locker.lock();
186 MTRACE(
"Device " << this->name <<
" LOCKed");
190 bool device_trezor_base::try_lock() {
191 MTRACE(
"Ask for LOCKING(try) for device " << this->name <<
" in thread ");
192 bool r = device_locker.try_lock();
194 MTRACE(
"Device " << this->name <<
" LOCKed(try)");
196 MDEBUG(
"Device " << this->name <<
" not LOCKed(try)");
202 void device_trezor_base::unlock() {
203 MTRACE(
"Ask for UNLOCKING for device " << this->name <<
" in thread ");
204 device_locker.unlock();
205 MTRACE(
"Device " << this->name <<
" UNLOCKed");
212 void device_trezor_base::require_connected()
const {
214 throw exc::NotConnectedException();
218 void device_trezor_base::require_initialized()
const {
220 throw exc::TrezorException(
"Device state not initialized");
223 if (m_features->has_bootloader_mode() && m_features->bootloader_mode()){
224 throw exc::TrezorException(
"Device is in the bootloader mode");
227 if (m_features->has_firmware_present() && !m_features->firmware_present()){
228 throw exc::TrezorException(
"Device has no firmware loaded");
232 if (!m_features->has_initialized() || !m_features->initialized()){
233 throw exc::TrezorException(
"Device is not initialized");
237 void device_trezor_base::call_ping_unsafe(){
238 auto pingMsg = std::make_shared<messages::management::Ping>();
239 pingMsg->set_message(
"PING");
241 auto success = this->client_exchange<messages::common::Success>(pingMsg);
246 void device_trezor_base::test_ping(){
250 this->call_ping_unsafe();
252 }
catch(exc::TrezorException
const& e){
253 MINFO(
"Trezor does not respond: " << e.what());
254 throw exc::DeviceNotResponsiveException(
std::string(
"Trezor not responding: ") + e.what());
258 void device_trezor_base::write_raw(
const google::protobuf::Message * msg){
261 this->get_transport()->write(*msg);
264 GenericMessage device_trezor_base::read_raw(){
266 std::shared_ptr<google::protobuf::Message> msg_resp;
267 hw::trezor::messages::MessageType msg_resp_type;
269 this->get_transport()->read(msg_resp, &msg_resp_type);
270 return GenericMessage(msg_resp_type, msg_resp);
273 GenericMessage device_trezor_base::call_raw(
const google::protobuf::Message * msg) {
278 bool device_trezor_base::message_handler(GenericMessage & input){
283 if (m_last_msg_type == messages::MessageType_ButtonRequest){
286 m_last_msg_type = input.m_type;
288 switch(input.m_type){
289 case messages::MessageType_ButtonRequest:
290 on_button_request(input, dynamic_cast<const messages::common::ButtonRequest*>(input.m_msg.get()));
292 case messages::MessageType_PassphraseRequest:
293 on_passphrase_request(input, dynamic_cast<const messages::common::PassphraseRequest*>(input.m_msg.get()));
295 case messages::MessageType_PassphraseStateRequest:
296 on_passphrase_state_request(input, dynamic_cast<const messages::common::PassphraseStateRequest*>(input.m_msg.get()));
298 case messages::MessageType_PinMatrixRequest:
299 on_pin_request(input, dynamic_cast<const messages::common::PinMatrixRequest*>(input.m_msg.get()));
306 void device_trezor_base::ensure_derivation_path() noexcept {
307 if (m_wallet_deriv_path.empty()){
308 m_wallet_deriv_path.push_back(TREZOR_BIP44_HARDENED_ZERO);
312 void device_trezor_base::set_derivation_path(
const std::string &deriv_path){
313 this->m_wallet_deriv_path.clear();
315 if (deriv_path.empty() || deriv_path ==
"-"){
316 ensure_derivation_path();
322 std::vector<std::string> fields;
323 boost::split(fields, deriv_path, boost::is_any_of(
"/"));
326 boost::regex rgx(
"^([0-9]+)'?$");
329 this->m_wallet_deriv_path.reserve(fields.size());
331 const bool ok = boost::regex_match(cur.c_str(), match, rgx);
333 CHECK_AND_ASSERT_THROW_MES(match[0].length() > 0,
"Invalid wallet code: " << deriv_path <<
". Invalid path element: " << cur);
335 const unsigned long cidx = std::stoul(match[0].str()) | TREZOR_BIP44_HARDENED_ZERO;
336 this->m_wallet_deriv_path.push_back((
unsigned int)cidx);
344 bool device_trezor_base::ping() {
347 MINFO(
"Ping failed, device not connected");
352 this->call_ping_unsafe();
355 }
catch(std::exception
const& e) {
356 MERROR(
"Ping failed, exception thrown " << e.what());
358 MERROR(
"Ping failed, general exception thrown" << boost::current_exception_diagnostic_information());
364 void device_trezor_base::device_state_reset_unsafe()
367 auto initMsg = std::make_shared<messages::management::Initialize>();
369 if(!m_device_state.empty()) {
370 initMsg->set_allocated_state(&m_device_state);
373 m_features = this->client_exchange<messages::management::Features>(initMsg);
374 initMsg->release_state();
377 void device_trezor_base::device_state_reset()
380 device_state_reset_unsafe();
383 #ifdef WITH_TREZOR_DEBUGGING 384 #define TREZOR_CALLBACK(method, ...) do { \ 385 if (m_debug_callback) m_debug_callback->method(__VA_ARGS__); \ 386 if (m_callback) m_callback->method(__VA_ARGS__); \ 388 #define TREZOR_CALLBACK_GET(VAR, method, ...) do { \ 389 if (m_debug_callback) VAR = m_debug_callback->method(__VA_ARGS__); \ 390 if (m_callback) VAR = m_callback->method(__VA_ARGS__); \ 393 void device_trezor_base::setup_debug(){
398 if (!m_debug_callback){
400 auto debug_transport = m_transport->find_debug();
401 if (debug_transport) {
402 m_debug_callback = std::make_shared<trezor_debug_callback>(debug_transport);
404 MDEBUG(
"Transport does not have debug link option");
410 #define TREZOR_CALLBACK(method, ...) do { if (m_callback) m_callback->method(__VA_ARGS__); } while(0) 411 #define TREZOR_CALLBACK_GET(VAR, method, ...) VAR = (m_callback ? m_callback->method(__VA_ARGS__) : boost::none) 414 void device_trezor_base::on_button_request(GenericMessage & resp,
const messages::common::ButtonRequest * msg)
417 MDEBUG(
"on_button_request, code: " << msg->code());
419 TREZOR_CALLBACK(on_button_request, msg->code());
421 messages::common::ButtonAck ack;
427 void device_trezor_base::on_button_pressed()
429 TREZOR_CALLBACK(on_button_pressed);
432 void device_trezor_base::on_pin_request(GenericMessage & resp,
const messages::common::PinMatrixRequest * msg)
437 boost::optional<epee::wipeable_string> pin;
438 TREZOR_CALLBACK_GET(pin, on_pin_request);
445 messages::common::PinMatrixAck m;
447 m.set_pin(pin.get().data(), pin.get().size());
452 void device_trezor_base::on_passphrase_request(GenericMessage & resp,
const messages::common::PassphraseRequest * msg)
455 MDEBUG(
"on_passhprase_request, on device: " << msg->on_device());
456 boost::optional<epee::wipeable_string> passphrase;
457 TREZOR_CALLBACK_GET(passphrase, on_passphrase_request, msg->on_device());
459 if (!passphrase && m_passphrase){
460 passphrase = m_passphrase;
463 m_passphrase = boost::none;
465 messages::common::PassphraseAck m;
466 if (!msg->on_device() && passphrase){
468 m.set_passphrase(passphrase.get().data(), passphrase.get().size());
471 if (!m_device_state.empty()){
472 m.set_allocated_state(&m_device_state);
479 void device_trezor_base::on_passphrase_state_request(GenericMessage & resp,
const messages::common::PassphraseStateRequest * msg)
481 MDEBUG(
"on_passhprase_state_request");
484 m_device_state = msg->state();
485 messages::common::PassphraseStateAck m;
489 #ifdef WITH_TREZOR_DEBUGGING 490 void device_trezor_base::wipe_device()
492 auto msg = std::make_shared<messages::management::WipeDevice>();
493 auto ret = client_exchange<messages::common::Success>(msg);
498 void device_trezor_base::init_device()
500 auto msg = std::make_shared<messages::management::Initialize>();
501 m_features = client_exchange<messages::management::Features>(msg);
506 bool skip_checksum,
bool expand)
508 if (m_features && m_features->initialized()){
509 throw std::runtime_error(
"Device is initialized already. Call device.wipe() and try again.");
512 auto msg = std::make_shared<messages::management::LoadDevice>();
513 msg->set_mnemonic(mnemonic);
515 msg->set_passphrase_protection(passphrase_protection);
516 msg->set_label(label);
517 msg->set_language(language);
518 msg->set_skip_checksum(skip_checksum);
519 auto ret = client_exchange<messages::common::Success>(msg);
525 trezor_debug_callback::trezor_debug_callback(std::shared_ptr<Transport> & debug_transport){
526 m_debug_link = std::make_shared<DebugLink>();
527 m_debug_link->init(debug_transport);
530 void trezor_debug_callback::on_button_request(
uint64_t code) {
531 if (m_debug_link) m_debug_link->press_yes();
534 boost::optional<epee::wipeable_string> trezor_debug_callback::on_pin_request() {
538 boost::optional<epee::wipeable_string> trezor_debug_callback::on_passphrase_request(
bool on_device) {
542 void trezor_debug_callback::on_passphrase_state_request(
const std::string &
state) {
546 void trezor_debug_callback::on_disconnect(){
547 if (m_debug_link) m_debug_link->close();
551 #endif //WITH_DEVICE_TREZOR
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
void enumerate(t_transport_vect &res)
unsigned __int64 uint64_t
void sort_transports_by_env(t_transport_vect &res)
expect< void > success() noexcept
#define TREZOR_AUTO_LOCK_CMD()
std::vector< std::shared_ptr< Transport > > t_transport_vect
#define TREZOR_AUTO_LOCK_DEVICE()