diff options
Diffstat (limited to 'src/device_trezor')
-rw-r--r-- | src/device_trezor/CMakeLists.txt | 8 | ||||
-rw-r--r-- | src/device_trezor/device_trezor.cpp | 377 | ||||
-rw-r--r-- | src/device_trezor/device_trezor.hpp | 100 | ||||
-rw-r--r-- | src/device_trezor/device_trezor_base.cpp | 149 | ||||
-rw-r--r-- | src/device_trezor/device_trezor_base.hpp | 77 | ||||
-rw-r--r-- | src/device_trezor/trezor.hpp | 2 | ||||
-rw-r--r-- | src/device_trezor/trezor/debug_link.cpp | 90 | ||||
-rw-r--r-- | src/device_trezor/trezor/debug_link.hpp | 93 | ||||
-rw-r--r-- | src/device_trezor/trezor/exceptions.hpp | 2 | ||||
-rw-r--r-- | src/device_trezor/trezor/messages_map.cpp | 13 | ||||
-rw-r--r-- | src/device_trezor/trezor/messages_map.hpp | 8 | ||||
-rw-r--r-- | src/device_trezor/trezor/protocol.cpp | 441 | ||||
-rw-r--r-- | src/device_trezor/trezor/protocol.hpp | 41 | ||||
-rw-r--r-- | src/device_trezor/trezor/tools/build_protob.py | 10 | ||||
-rw-r--r-- | src/device_trezor/trezor/transport.cpp | 152 | ||||
-rw-r--r-- | src/device_trezor/trezor/transport.hpp | 21 | ||||
-rw-r--r-- | src/device_trezor/trezor/trezor_defs.hpp | 2 |
17 files changed, 1342 insertions, 244 deletions
diff --git a/src/device_trezor/CMakeLists.txt b/src/device_trezor/CMakeLists.txt index 7f979389a..250939da7 100644 --- a/src/device_trezor/CMakeLists.txt +++ b/src/device_trezor/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2017, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -67,6 +67,12 @@ set(trezor_private_headers) if(DEVICE_TREZOR_READY) message(STATUS "Trezor support enabled") + if(USE_DEVICE_TREZOR_DEBUG) + list(APPEND trezor_headers trezor/debug_link.hpp trezor/messages/messages-debug.pb.h) + list(APPEND trezor_sources trezor/debug_link.cpp trezor/messages/messages-debug.pb.cc) + message(STATUS "Trezor debugging enabled") + endif() + monero_private_headers(device_trezor ${device_private_headers}) diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp index 8868fb995..b4a80cf2c 100644 --- a/src/device_trezor/device_trezor.cpp +++ b/src/device_trezor/device_trezor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -57,7 +57,9 @@ namespace trezor { } device_trezor::device_trezor() { - + m_live_refresh_in_progress = false; + m_live_refresh_enabled = true; + m_live_refresh_thread_running = false; } device_trezor::~device_trezor() { @@ -69,6 +71,89 @@ namespace trezor { } } + bool device_trezor::init() + { + m_live_refresh_in_progress = false; + bool r = device_trezor_base::init(); + if (r && !m_live_refresh_thread) + { + m_live_refresh_thread_running = true; + m_live_refresh_thread.reset(new boost::thread(boost::bind(&device_trezor::live_refresh_thread_main, this))); + } + return r; + } + + bool device_trezor::release() + { + m_live_refresh_in_progress = false; + m_live_refresh_thread_running = false; + if (m_live_refresh_thread) + { + m_live_refresh_thread->join(); + m_live_refresh_thread = nullptr; + } + return device_trezor_base::release(); + } + + bool device_trezor::disconnect() + { + m_live_refresh_in_progress = false; + return device_trezor_base::disconnect(); + } + + void device_trezor::device_state_reset_unsafe() + { + require_connected(); + if (m_live_refresh_in_progress) + { + try + { + live_refresh_finish_unsafe(); + } + catch(const std::exception & e) + { + MERROR("Live refresh could not be terminated: " << e.what()); + } + } + + m_live_refresh_in_progress = false; + device_trezor_base::device_state_reset_unsafe(); + } + + void device_trezor::live_refresh_thread_main() + { + while(m_live_refresh_thread_running) + { + boost::this_thread::sleep_for(boost::chrono::milliseconds(100)); + if (!m_live_refresh_in_progress) + { + continue; + } + + TREZOR_AUTO_LOCK_DEVICE(); + if (!m_transport || !m_live_refresh_in_progress) + { + continue; + } + + auto current_time = std::chrono::steady_clock::now(); + if (current_time - m_last_live_refresh_time <= std::chrono::seconds(20)) + { + continue; + } + + MTRACE("Closing live refresh process due to inactivity"); + try + { + live_refresh_finish(); + } + catch(const std::exception &e) + { + MWARNING("Live refresh auto-finish failed: " << e.what()); + } + } + } + /* ======================================================================= */ /* WALLET & ADDRESS */ /* ======================================================================= */ @@ -97,7 +182,14 @@ namespace trezor { auto res = get_view_key(); CHECK_AND_ASSERT_MES(res->watch_key().size() == 32, false, "Trezor returned invalid view key"); + // Trezor does not make use of spendkey of the device API. + // Ledger loads encrypted spendkey, Trezor loads null key (never leaves device). + // In the test (debugging mode) we need to leave this field intact as it is already set by + // the debugging code and need to remain same for the testing purposes. +#ifndef WITH_TREZOR_DEBUGGING spendkey = crypto::null_skey; // not given +#endif + memcpy(viewkey.data, res->watch_key().data(), 32); return true; @@ -119,7 +211,7 @@ namespace trezor { std::shared_ptr<messages::monero::MoneroAddress> device_trezor::get_address( const boost::optional<std::vector<uint32_t>> & path, const boost::optional<cryptonote::network_type> & network_type){ - AUTO_LOCK_CMD(); + TREZOR_AUTO_LOCK_CMD(); require_connected(); device_state_reset_unsafe(); require_initialized(); @@ -135,7 +227,7 @@ namespace trezor { std::shared_ptr<messages::monero::MoneroWatchKey> device_trezor::get_view_key( const boost::optional<std::vector<uint32_t>> & path, const boost::optional<cryptonote::network_type> & network_type){ - AUTO_LOCK_CMD(); + TREZOR_AUTO_LOCK_CMD(); require_connected(); device_state_reset_unsafe(); require_initialized(); @@ -148,11 +240,43 @@ namespace trezor { return response; } + bool device_trezor::is_get_tx_key_supported() const + { + require_initialized(); + return get_version() > pack_version(2, 0, 10); + } + + void device_trezor::load_tx_key_data(::hw::device_cold::tx_key_data_t & res, const std::string & tx_aux_data) + { + protocol::tx::load_tx_key_data(res, tx_aux_data); + } + + void device_trezor::get_tx_key( + std::vector<::crypto::secret_key> & tx_keys, + const ::hw::device_cold::tx_key_data_t & tx_aux_data, + const ::crypto::secret_key & view_key_priv) + { + TREZOR_AUTO_LOCK_CMD(); + require_connected(); + device_state_reset_unsafe(); + require_initialized(); + + auto req = protocol::tx::get_tx_key(tx_aux_data); + this->set_msg_addr<messages::monero::MoneroGetTxKeyRequest>(req.get()); + + auto response = this->client_exchange<messages::monero::MoneroGetTxKeyAck>(req); + MTRACE("Get TX key response received"); + + protocol::tx::get_tx_key_ack(tx_keys, tx_aux_data.tx_prefix_hash, view_key_priv, response); + } + void device_trezor::ki_sync(wallet_shim * wallet, const std::vector<tools::wallet2::transfer_details> & transfers, hw::device_cold::exported_key_image & ski) { - AUTO_LOCK_CMD(); +#define EVENT_PROGRESS(P) do { if (m_callback) {(m_callback)->on_progress(device_cold::op_progress(P)); } }while(0) + + TREZOR_AUTO_LOCK_CMD(); require_connected(); device_state_reset_unsafe(); require_initialized(); @@ -164,6 +288,7 @@ namespace trezor { protocol::ki::key_image_data(wallet, transfers, mtds); protocol::ki::generate_commitment(mtds, transfers, req); + EVENT_PROGRESS(0.); this->set_msg_addr<messages::monero::MoneroKeyImageExportInitRequest>(req.get()); auto ack1 = this->client_exchange<messages::monero::MoneroKeyImageExportInitAck>(req); @@ -187,27 +312,160 @@ namespace trezor { } MTRACE("Batch " << cur << " / " << num_batches << " batches processed"); + EVENT_PROGRESS((double)cur * batch_size / mtds.size()); } + EVENT_PROGRESS(1.); auto final_req = std::make_shared<messages::monero::MoneroKeyImageSyncFinalRequest>(); auto final_ack = this->client_exchange<messages::monero::MoneroKeyImageSyncFinalAck>(final_req); ski.reserve(kis.size()); for(auto & sub : kis){ - char buff[32*3]; + ::crypto::signature sig{}; + ::crypto::key_image ki; + char buff[sizeof(ki.data)*3]; + + size_t buff_len = sizeof(buff); + protocol::crypto::chacha::decrypt(sub.blob().data(), sub.blob().size(), reinterpret_cast<const uint8_t *>(final_ack->enc_key().data()), - reinterpret_cast<const uint8_t *>(sub.iv().data()), buff); + reinterpret_cast<const uint8_t *>(sub.iv().data()), buff, &buff_len); + CHECK_AND_ASSERT_THROW_MES(buff_len == sizeof(buff), "Plaintext size invalid"); - ::crypto::signature sig{}; - ::crypto::key_image ki; - memcpy(ki.data, buff, 32); - memcpy(sig.c.data, buff + 32, 32); - memcpy(sig.r.data, buff + 64, 32); + memcpy(ki.data, buff, sizeof(ki.data)); + memcpy(sig.c.data, buff + sizeof(ki.data), sizeof(ki.data)); + memcpy(sig.r.data, buff + 2*sizeof(ki.data), sizeof(ki.data)); ski.push_back(std::make_pair(ki, sig)); } +#undef EVENT_PROGRESS + } + + bool device_trezor::is_live_refresh_supported() const + { + require_initialized(); + return get_version() > pack_version(2, 0, 10); } + bool device_trezor::is_live_refresh_enabled() const + { + return is_live_refresh_supported() && (mode == NONE || mode == TRANSACTION_PARSE) && m_live_refresh_enabled; + } + + bool device_trezor::has_ki_live_refresh() const + { + try{ + return is_live_refresh_enabled(); + } catch(const std::exception & e){ + MERROR("Could not detect if live refresh is enabled: " << e.what()); + } + return false; + } + + void device_trezor::live_refresh_start() + { + TREZOR_AUTO_LOCK_CMD(); + require_connected(); + live_refresh_start_unsafe(); + } + + void device_trezor::live_refresh_start_unsafe() + { + device_state_reset_unsafe(); + require_initialized(); + + auto req = std::make_shared<messages::monero::MoneroLiveRefreshStartRequest>(); + this->set_msg_addr<messages::monero::MoneroLiveRefreshStartRequest>(req.get()); + this->client_exchange<messages::monero::MoneroLiveRefreshStartAck>(req); + m_live_refresh_in_progress = true; + m_last_live_refresh_time = std::chrono::steady_clock::now(); + } + + void device_trezor::live_refresh( + const ::crypto::secret_key & view_key_priv, + const crypto::public_key& out_key, + const crypto::key_derivation& recv_derivation, + size_t real_output_index, + const cryptonote::subaddress_index& received_index, + cryptonote::keypair& in_ephemeral, + crypto::key_image& ki + ) + { + TREZOR_AUTO_LOCK_CMD(); + require_connected(); + + if (!m_live_refresh_in_progress) + { + live_refresh_start_unsafe(); + } + + m_last_live_refresh_time = std::chrono::steady_clock::now(); + + auto req = std::make_shared<messages::monero::MoneroLiveRefreshStepRequest>(); + req->set_out_key(out_key.data, 32); + req->set_recv_deriv(recv_derivation.data, 32); + req->set_real_out_idx(real_output_index); + req->set_sub_addr_major(received_index.major); + req->set_sub_addr_minor(received_index.minor); + + auto ack = this->client_exchange<messages::monero::MoneroLiveRefreshStepAck>(req); + protocol::ki::live_refresh_ack(view_key_priv, out_key, ack, in_ephemeral, ki); + } + + void device_trezor::live_refresh_finish_unsafe() + { + auto req = std::make_shared<messages::monero::MoneroLiveRefreshFinalRequest>(); + this->client_exchange<messages::monero::MoneroLiveRefreshFinalAck>(req); + m_live_refresh_in_progress = false; + } + + void device_trezor::live_refresh_finish() + { + TREZOR_AUTO_LOCK_CMD(); + require_connected(); + if (m_live_refresh_in_progress) + { + live_refresh_finish_unsafe(); + } + } + + void device_trezor::computing_key_images(bool started) + { + try + { + if (!is_live_refresh_enabled()) + { + return; + } + + // React only on termination as the process can auto-start itself. + if (!started && m_live_refresh_in_progress) + { + live_refresh_finish(); + } + } + catch(const std::exception & e) + { + MWARNING("KI computation state change failed, started: " << started << ", e: " << e.what()); + } + } + + bool device_trezor::compute_key_image( + const ::cryptonote::account_keys& ack, + const ::crypto::public_key& out_key, + const ::crypto::key_derivation& recv_derivation, + size_t real_output_index, + const ::cryptonote::subaddress_index& received_index, + ::cryptonote::keypair& in_ephemeral, + ::crypto::key_image& ki) + { + if (!is_live_refresh_enabled()) + { + return false; + } + + live_refresh(ack.m_view_secret_key, out_key, recv_derivation, real_output_index, received_index, in_ephemeral, ki); + return true; + } void device_trezor::tx_sign(wallet_shim * wallet, const tools::wallet2::unsigned_tx_set & unsigned_tx, @@ -215,7 +473,15 @@ namespace trezor { hw::tx_aux_data & aux_data) { CHECK_AND_ASSERT_THROW_MES(unsigned_tx.transfers.first == 0, "Unsuported non zero offset"); - size_t num_tx = unsigned_tx.txes.size(); + + TREZOR_AUTO_LOCK_CMD(); + require_connected(); + device_state_reset_unsafe(); + require_initialized(); + transaction_versions_check(unsigned_tx, aux_data); + + const size_t num_tx = unsigned_tx.txes.size(); + m_num_transations_to_sign = num_tx; signed_tx.key_images.clear(); signed_tx.key_images.resize(unsigned_tx.transfers.second.size()); @@ -260,6 +526,10 @@ namespace trezor { cpend.key_images = key_images; // KI sync + for(size_t cidx=0, trans_max=unsigned_tx.transfers.second.size(); cidx < trans_max; ++cidx){ + signed_tx.key_images[cidx] = unsigned_tx.transfers.second[cidx].m_key_image; + } + size_t num_sources = cdata.tx_data.sources.size(); CHECK_AND_ASSERT_THROW_MES(num_sources == cdata.source_permutation.size(), "Invalid permutation size"); CHECK_AND_ASSERT_THROW_MES(num_sources == cdata.tx.vin.size(), "Invalid tx.vin size"); @@ -269,12 +539,19 @@ namespace trezor { CHECK_AND_ASSERT_THROW_MES(src_idx < cdata.tx.vin.size(), "Invalid idx_mapped"); size_t idx_map_src = cdata.tx_data.selected_transfers[idx_mapped]; - auto vini = boost::get<cryptonote::txin_to_key>(cdata.tx.vin[src_idx]); + CHECK_AND_ASSERT_THROW_MES(idx_map_src >= unsigned_tx.transfers.first, "Invalid offset"); + idx_map_src -= unsigned_tx.transfers.first; CHECK_AND_ASSERT_THROW_MES(idx_map_src < signed_tx.key_images.size(), "Invalid key image index"); + + const auto vini = boost::get<cryptonote::txin_to_key>(cdata.tx.vin[src_idx]); signed_tx.key_images[idx_map_src] = vini.k_image; } } + + if (m_callback){ + m_callback->on_progress(device_cold::tx_progress(m_num_transations_to_sign, m_num_transations_to_sign, 1, 1, 1, 1)); + } } void device_trezor::tx_sign(wallet_shim * wallet, @@ -283,10 +560,16 @@ namespace trezor { hw::tx_aux_data & aux_data, std::shared_ptr<protocol::tx::Signer> & signer) { - AUTO_LOCK_CMD(); +#define EVENT_PROGRESS(S, SUB, SUBMAX) do { if (m_callback) { \ + (m_callback)->on_progress(device_cold::tx_progress(idx, m_num_transations_to_sign, S, 10, SUB, SUBMAX)); \ +} }while(0) + require_connected(); - device_state_reset_unsafe(); + if (idx > 0) + device_state_reset_unsafe(); + require_initialized(); + EVENT_PROGRESS(0, 1, 1); CHECK_AND_ASSERT_THROW_MES(idx < unsigned_tx.txes.size(), "Invalid transaction index"); signer = std::make_shared<protocol::tx::Signer>(wallet, &unsigned_tx, idx, &aux_data); @@ -298,6 +581,7 @@ namespace trezor { auto init_msg = signer->step_init(); this->set_msg_addr(init_msg.get()); transaction_pre_check(init_msg); + EVENT_PROGRESS(1, 1, 1); auto response = this->client_exchange<messages::monero::MoneroTransactionInitAck>(init_msg); signer->step_init_ack(response); @@ -307,6 +591,7 @@ namespace trezor { auto src = signer->step_set_input(cur_src); auto ack = this->client_exchange<messages::monero::MoneroTransactionSetInputAck>(src); signer->step_set_input_ack(ack); + EVENT_PROGRESS(2, cur_src, num_sources); } // Step: sort @@ -315,44 +600,82 @@ namespace trezor { auto perm_ack = this->client_exchange<messages::monero::MoneroTransactionInputsPermutationAck>(perm_req); signer->step_permutation_ack(perm_ack); } + EVENT_PROGRESS(3, 1, 1); // Step: input_vini - if (!signer->in_memory()){ - for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){ - auto src = signer->step_set_vini_input(cur_src); - auto ack = this->client_exchange<messages::monero::MoneroTransactionInputViniAck>(src); - signer->step_set_vini_input_ack(ack); - } + for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){ + auto src = signer->step_set_vini_input(cur_src); + auto ack = this->client_exchange<messages::monero::MoneroTransactionInputViniAck>(src); + signer->step_set_vini_input_ack(ack); + EVENT_PROGRESS(4, cur_src, num_sources); } // Step: all inputs set auto all_inputs_set = signer->step_all_inputs_set(); auto ack_all_inputs = this->client_exchange<messages::monero::MoneroTransactionAllInputsSetAck>(all_inputs_set); signer->step_all_inputs_set_ack(ack_all_inputs); + EVENT_PROGRESS(5, 1, 1); // Step: outputs for(size_t cur_dst = 0; cur_dst < num_outputs; ++cur_dst){ auto src = signer->step_set_output(cur_dst); auto ack = this->client_exchange<messages::monero::MoneroTransactionSetOutputAck>(src); signer->step_set_output_ack(ack); + + // If BP is offloaded to host, another step with computed BP may be needed. + auto offloaded_bp = signer->step_rsig(cur_dst); + if (offloaded_bp){ + auto bp_ack = this->client_exchange<messages::monero::MoneroTransactionSetOutputAck>(offloaded_bp); + signer->step_set_rsig_ack(ack); + } + + EVENT_PROGRESS(6, cur_dst, num_outputs); } // Step: all outs set auto all_out_set = signer->step_all_outs_set(); auto ack_all_out_set = this->client_exchange<messages::monero::MoneroTransactionAllOutSetAck>(all_out_set); signer->step_all_outs_set_ack(ack_all_out_set, *this); + EVENT_PROGRESS(7, 1, 1); // Step: sign each input for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){ auto src = signer->step_sign_input(cur_src); auto ack_sign = this->client_exchange<messages::monero::MoneroTransactionSignInputAck>(src); signer->step_sign_input_ack(ack_sign); + EVENT_PROGRESS(8, cur_src, num_sources); } // Step: final auto final_msg = signer->step_final(); auto ack_final = this->client_exchange<messages::monero::MoneroTransactionFinalAck>(final_msg); signer->step_final_ack(ack_final); + EVENT_PROGRESS(9, 1, 1); +#undef EVENT_PROGRESS + } + + void device_trezor::transaction_versions_check(const ::tools::wallet2::unsigned_tx_set & unsigned_tx, hw::tx_aux_data & aux_data) + { + auto trezor_version = get_version(); + unsigned client_version = 1; // default client version for tx + + if (trezor_version <= pack_version(2, 0, 10)){ + client_version = 0; + } + + if (aux_data.client_version){ + auto wanted_client_version = aux_data.client_version.get(); + if (wanted_client_version > client_version){ + throw exc::TrezorException("Trezor firmware 2.0.10 and lower does not support current transaction sign protocol. Please update."); + } else { + client_version = wanted_client_version; + } + } + aux_data.client_version = client_version; + + if (client_version == 0 && aux_data.bp_version && aux_data.bp_version.get() != 1){ + throw exc::TrezorException("Trezor firmware 2.0.10 and lower does not support current transaction sign protocol (BPv2+). Please update."); + } } void device_trezor::transaction_pre_check(std::shared_ptr<messages::monero::MoneroTransactionInitRequest> init_msg) @@ -362,13 +685,9 @@ namespace trezor { CHECK_AND_ASSERT_THROW_MES(m_features, "Device state not initialized"); // make sure the caller did not reset features const bool nonce_required = init_msg->tsx_data().has_payment_id() && init_msg->tsx_data().payment_id().size() > 0; - if (nonce_required){ + if (nonce_required && init_msg->tsx_data().payment_id().size() == 8){ // Versions 2.0.9 and lower do not support payment ID - CHECK_AND_ASSERT_THROW_MES(m_features->has_major_version() && m_features->has_minor_version() && m_features->has_patch_version(), "Invalid Trezor firmware version information"); - const uint32_t vma = m_features->major_version(); - const uint32_t vmi = m_features->minor_version(); - const uint32_t vpa = m_features->patch_version(); - if (vma < 2 || (vma == 2 && vmi == 0 && vpa <= 9)) { + if (get_version() <= pack_version(2, 0, 9)) { throw exc::TrezorException("Trezor firmware 2.0.9 and lower does not support transactions with short payment IDs or integrated addresses. Please update."); } } @@ -393,7 +712,7 @@ namespace trezor { const bool nonce_required = tdata.tsx_data.has_payment_id() && tdata.tsx_data.payment_id().size() > 0; const bool has_nonce = cryptonote::find_tx_extra_field_by_type(tx_extra_fields, nonce); - CHECK_AND_ASSERT_THROW_MES(has_nonce == nonce_required, "Transaction nonce presence inconsistent"); + CHECK_AND_ASSERT_THROW_MES(has_nonce || !nonce_required, "Transaction nonce not present"); if (nonce_required){ const std::string & payment_id = tdata.tsx_data.payment_id(); diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp index 1f08be887..0e91847dc 100644 --- a/src/device_trezor/device_trezor.hpp +++ b/src/device_trezor/device_trezor.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -30,18 +30,21 @@ #ifndef MONERO_DEVICE_TREZOR_H #define MONERO_DEVICE_TREZOR_H +#include "trezor.hpp" +#include "device/device.hpp" +#ifdef WITH_DEVICE_TREZOR #include <cstddef> #include <string> -#include "device/device.hpp" -#include "device/device_default.hpp" -#include "device/device_cold.hpp" #include <boost/scope_exit.hpp> #include <boost/thread/mutex.hpp> #include <boost/thread/recursive_mutex.hpp> + +#include "device/device_default.hpp" +#include "device/device_cold.hpp" #include "cryptonote_config.h" -#include "trezor.hpp" #include "device_trezor_base.hpp" +#endif namespace hw { namespace trezor { @@ -57,8 +60,29 @@ namespace trezor { */ class device_trezor : public hw::trezor::device_trezor_base, public hw::device_cold { protected: + std::atomic<bool> m_live_refresh_in_progress; + std::chrono::steady_clock::time_point m_last_live_refresh_time; + std::unique_ptr<boost::thread> m_live_refresh_thread; + std::atomic<bool> m_live_refresh_thread_running; + bool m_live_refresh_enabled; + size_t m_num_transations_to_sign; + + void transaction_versions_check(const ::tools::wallet2::unsigned_tx_set & unsigned_tx, hw::tx_aux_data & aux_data); void transaction_pre_check(std::shared_ptr<messages::monero::MoneroTransactionInitRequest> init_msg); void transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data); + void device_state_reset_unsafe() override; + void live_refresh_start_unsafe(); + void live_refresh_finish_unsafe(); + void live_refresh_thread_main(); + + /** + * Signs particular transaction idx in the unsigned set, keeps state in the signer + */ + virtual void tx_sign(wallet_shim * wallet, + const ::tools::wallet2::unsigned_tx_set & unsigned_tx, + size_t idx, + hw::tx_aux_data & aux_data, + std::shared_ptr<protocol::tx::Signer> & signer); public: device_trezor(); @@ -69,11 +93,17 @@ namespace trezor { explicit operator bool() const override {return true;} + bool init() override; + bool release() override; + bool disconnect() override; + device_protocol_t device_protocol() const override { return PROTOCOL_COLD; }; bool has_ki_cold_sync() const override { return true; } bool has_tx_cold_sign() const override { return true; } void set_network_type(cryptonote::network_type network_type) override { this->network_type = network_type; } + void set_live_refresh_enabled(bool enabled) { m_live_refresh_enabled = enabled; } + bool live_refresh_enabled() const { return m_live_refresh_enabled; } /* ======================================================================= */ /* WALLET & ADDRESS */ @@ -100,20 +130,68 @@ namespace trezor { const boost::optional<cryptonote::network_type> & network_type = boost::none); /** + * Get_tx_key support check + */ + bool is_get_tx_key_supported() const override; + + /** + * Loads tx aux data + */ + void load_tx_key_data(::hw::device_cold::tx_key_data_t & res, const std::string & tx_aux_data) override; + + /** + * TX key load with the Trezor + */ + void get_tx_key( + std::vector<::crypto::secret_key> & tx_keys, + const ::hw::device_cold::tx_key_data_t & tx_aux_data, + const ::crypto::secret_key & view_key_priv) override; + + /** * Key image sync with the Trezor. */ void ki_sync(wallet_shim * wallet, const std::vector<::tools::wallet2::transfer_details> & transfers, hw::device_cold::exported_key_image & ski) override; + bool is_live_refresh_supported() const override; + + bool is_live_refresh_enabled() const; + + bool has_ki_live_refresh() const override; + + void live_refresh_start() override; + + void live_refresh( + const ::crypto::secret_key & view_key_priv, + const crypto::public_key& out_key, + const crypto::key_derivation& recv_derivation, + size_t real_output_index, + const cryptonote::subaddress_index& received_index, + cryptonote::keypair& in_ephemeral, + crypto::key_image& ki + ) override; + + void live_refresh_finish() override; + /** - * Signs particular transaction idx in the unsigned set, keeps state in the signer + * Letting device know the KI computation started / ended. + * During refresh */ - void tx_sign(wallet_shim * wallet, - const ::tools::wallet2::unsigned_tx_set & unsigned_tx, - size_t idx, - hw::tx_aux_data & aux_data, - std::shared_ptr<protocol::tx::Signer> & signer); + void computing_key_images(bool started) override; + + /** + * Implements hw::device interface + * called from generate_key_image_helper_precomp() + */ + bool compute_key_image( + const ::cryptonote::account_keys& ack, + const ::crypto::public_key& out_key, + const ::crypto::key_derivation& recv_derivation, + size_t real_output_index, + const ::cryptonote::subaddress_index& received_index, + ::cryptonote::keypair& in_ephemeral, + ::crypto::key_image& ki) override; /** * Signs unsigned transaction with the Trezor. diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp index 5071932ee..f3d15c5e2 100644 --- a/src/device_trezor/device_trezor_base.cpp +++ b/src/device_trezor/device_trezor_base.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -44,7 +44,9 @@ namespace trezor { const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080}; device_trezor_base::device_trezor_base(): m_callback(nullptr) { - +#ifdef WITH_TREZOR_DEBUGGING + m_debug = false; +#endif } device_trezor_base::~device_trezor_base() { @@ -107,6 +109,7 @@ namespace trezor { disconnect(); // Enumerate all available devices + TREZOR_AUTO_LOCK_DEVICE(); try { hw::trezor::t_transport_vect trans; @@ -130,6 +133,10 @@ namespace trezor { } m_transport->open(); + +#ifdef WITH_TREZOR_DEBUGGING + setup_debug(); +#endif return true; } catch(std::exception const& e){ @@ -139,6 +146,7 @@ namespace trezor { } bool device_trezor_base::disconnect() { + TREZOR_AUTO_LOCK_DEVICE(); m_device_state.clear(); m_features.reset(); @@ -153,6 +161,13 @@ namespace trezor { return false; } } + +#ifdef WITH_TREZOR_DEBUGGING + if (m_debug_callback) { + m_debug_callback->on_disconnect(); + m_debug_callback = nullptr; + } +#endif return true; } @@ -190,13 +205,13 @@ namespace trezor { /* Helpers */ /* ======================================================================= */ - void device_trezor_base::require_connected(){ + void device_trezor_base::require_connected() const { if (!m_transport){ throw exc::NotConnectedException(); } } - void device_trezor_base::require_initialized(){ + void device_trezor_base::require_initialized() const { if (!m_features){ throw exc::TrezorException("Device state not initialized"); } @@ -317,7 +332,7 @@ namespace trezor { /* ======================================================================= */ bool device_trezor_base::ping() { - AUTO_LOCK_CMD(); + TREZOR_AUTO_LOCK_CMD(); if (!m_transport){ MINFO("Ping failed, device not connected"); return false; @@ -351,10 +366,41 @@ namespace trezor { void device_trezor_base::device_state_reset() { - AUTO_LOCK_CMD(); + TREZOR_AUTO_LOCK_CMD(); device_state_reset_unsafe(); } +#ifdef WITH_TREZOR_DEBUGGING +#define TREZOR_CALLBACK(method, ...) do { \ + if (m_debug_callback) m_debug_callback->method(__VA_ARGS__); \ + if (m_callback) m_callback->method(__VA_ARGS__); \ +}while(0) +#define TREZOR_CALLBACK_GET(VAR, method, ...) do { \ + if (m_debug_callback) VAR = m_debug_callback->method(__VA_ARGS__); \ + if (m_callback) VAR = m_callback->method(__VA_ARGS__); \ +}while(0) + + void device_trezor_base::setup_debug(){ + if (!m_debug){ + return; + } + + if (!m_debug_callback){ + CHECK_AND_ASSERT_THROW_MES(m_transport, "Transport does not exist"); + auto debug_transport = m_transport->find_debug(); + if (debug_transport) { + m_debug_callback = std::make_shared<trezor_debug_callback>(debug_transport); + } else { + MDEBUG("Transport does not have debug link option"); + } + } + } + +#else +#define TREZOR_CALLBACK(method, ...) do { if (m_callback) m_callback->method(__VA_ARGS__); } while(0) +#define TREZOR_CALLBACK_GET(VAR, method, ...) VAR = (m_callback ? m_callback->method(__VA_ARGS__) : boost::none) +#endif + void device_trezor_base::on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg) { CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); @@ -363,10 +409,7 @@ namespace trezor { messages::common::ButtonAck ack; write_raw(&ack); - if (m_callback){ - m_callback->on_button_request(); - } - + TREZOR_CALLBACK(on_button_request, msg->code()); resp = read_raw(); } @@ -375,15 +418,18 @@ namespace trezor { MDEBUG("on_pin_request"); CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); - epee::wipeable_string pin; + boost::optional<epee::wipeable_string> pin; + TREZOR_CALLBACK_GET(pin, on_pin_request); - if (m_callback){ - m_callback->on_pin_request(pin); + if (!pin && m_pin){ + pin = m_pin; } // TODO: remove PIN from memory messages::common::PinMatrixAck m; - m.set_pin(pin.data(), pin.size()); + if (pin) { + m.set_pin(pin.get().data(), pin.get().size()); + } resp = call_raw(&m); } @@ -391,16 +437,19 @@ namespace trezor { { CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); MDEBUG("on_passhprase_request, on device: " << msg->on_device()); - epee::wipeable_string passphrase; + boost::optional<epee::wipeable_string> passphrase; + TREZOR_CALLBACK_GET(passphrase, on_passphrase_request, msg->on_device()); - if (m_callback){ - m_callback->on_passphrase_request(msg->on_device(), passphrase); + if (!passphrase && m_passphrase){ + passphrase = m_passphrase; } + m_passphrase = boost::none; + messages::common::PassphraseAck m; - if (!msg->on_device()){ + if (!msg->on_device() && passphrase){ // TODO: remove passphrase from memory - m.set_passphrase(passphrase.data(), passphrase.size()); + m.set_passphrase(passphrase.get().data(), passphrase.get().size()); } if (!m_device_state.empty()){ @@ -421,5 +470,67 @@ namespace trezor { resp = call_raw(&m); } +#ifdef WITH_TREZOR_DEBUGGING + void device_trezor_base::wipe_device() + { + auto msg = std::make_shared<messages::management::WipeDevice>(); + auto ret = client_exchange<messages::common::Success>(msg); + (void)ret; + init_device(); + } + + void device_trezor_base::init_device() + { + auto msg = std::make_shared<messages::management::Initialize>(); + m_features = client_exchange<messages::management::Features>(msg); + } + + void device_trezor_base::load_device(const std::string & mnemonic, const std::string & pin, + bool passphrase_protection, const std::string & label, const std::string & language, + bool skip_checksum, bool expand) + { + if (m_features && m_features->initialized()){ + throw std::runtime_error("Device is initialized already. Call device.wipe() and try again."); + } + + auto msg = std::make_shared<messages::management::LoadDevice>(); + msg->set_mnemonic(mnemonic); + msg->set_pin(pin); + msg->set_passphrase_protection(passphrase_protection); + msg->set_label(label); + msg->set_language(language); + msg->set_skip_checksum(skip_checksum); + auto ret = client_exchange<messages::common::Success>(msg); + (void)ret; + + init_device(); + } + + trezor_debug_callback::trezor_debug_callback(std::shared_ptr<Transport> & debug_transport){ + m_debug_link = std::make_shared<DebugLink>(); + m_debug_link->init(debug_transport); + } + + void trezor_debug_callback::on_button_request(uint64_t code) { + if (m_debug_link) m_debug_link->press_yes(); + } + + boost::optional<epee::wipeable_string> trezor_debug_callback::on_pin_request() { + return boost::none; + } + + boost::optional<epee::wipeable_string> trezor_debug_callback::on_passphrase_request(bool on_device) { + return boost::none; + } + + void trezor_debug_callback::on_passphrase_state_request(const std::string &state) { + + } + + void trezor_debug_callback::on_disconnect(){ + if (m_debug_link) m_debug_link->close(); + } +#endif + #endif //WITH_DEVICE_TREZOR }} diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp index 3c35e8aca..8c3c14b29 100644 --- a/src/device_trezor/device_trezor_base.hpp +++ b/src/device_trezor/device_trezor_base.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -42,21 +42,43 @@ #include "cryptonote_config.h" #include "trezor.hpp" +#ifdef WITH_TREZOR_DEBUGGING +#include "trezor/debug_link.hpp" +#endif + //automatic lock one more level on device ensuring the current thread is allowed to use it -#define AUTO_LOCK_CMD() \ +#define TREZOR_AUTO_LOCK_CMD() \ /* lock both mutexes without deadlock*/ \ boost::lock(device_locker, command_locker); \ /* make sure both already-locked mutexes are unlocked at the end of scope */ \ boost::lock_guard<boost::recursive_mutex> lock1(device_locker, boost::adopt_lock); \ boost::lock_guard<boost::mutex> lock2(command_locker, boost::adopt_lock) - +#define TREZOR_AUTO_LOCK_DEVICE() boost::lock_guard<boost::recursive_mutex> lock1_device(device_locker) + namespace hw { namespace trezor { #ifdef WITH_DEVICE_TREZOR class device_trezor_base; +#ifdef WITH_TREZOR_DEBUGGING + class trezor_debug_callback : public hw::i_device_callback { + public: + trezor_debug_callback()=default; + explicit trezor_debug_callback(std::shared_ptr<Transport> & debug_transport); + + void on_button_request(uint64_t code=0) override; + boost::optional<epee::wipeable_string> on_pin_request() override; + boost::optional<epee::wipeable_string> on_passphrase_request(bool on_device) override; + void on_passphrase_state_request(const std::string &state); + void on_disconnect(); + protected: + std::shared_ptr<DebugLink> m_debug_link; + }; + +#endif + /** * TREZOR device template with basic functions */ @@ -74,18 +96,27 @@ namespace trezor { std::vector<unsigned int> m_wallet_deriv_path; std::string m_device_state; // returned after passphrase entry, session std::shared_ptr<messages::management::Features> m_features; // features from the last device reset + boost::optional<epee::wipeable_string> m_pin; + boost::optional<epee::wipeable_string> m_passphrase; cryptonote::network_type network_type; +#ifdef WITH_TREZOR_DEBUGGING + std::shared_ptr<trezor_debug_callback> m_debug_callback; + bool m_debug; + + void setup_debug(); +#endif + // // Internal methods // - void require_connected(); - void require_initialized(); + void require_connected() const; + void require_initialized() const; void call_ping_unsafe(); void test_ping(); - void device_state_reset_unsafe(); + virtual void device_state_reset_unsafe(); void ensure_derivation_path() noexcept; // Communication methods @@ -103,7 +134,7 @@ namespace trezor { * @throws UnexpectedMessageException if the response message type is different than expected. * Exception contains message type and the message itself. */ - template<class t_message> + template<class t_message=google::protobuf::Message> std::shared_ptr<t_message> client_exchange(const std::shared_ptr<const google::protobuf::Message> &req, const boost::optional<messages::MessageType> & resp_type = boost::none, @@ -229,8 +260,23 @@ namespace trezor { return m_features; } + uint64_t get_version() const { + CHECK_AND_ASSERT_THROW_MES(m_features, "Features not loaded"); + CHECK_AND_ASSERT_THROW_MES(m_features->has_major_version() && m_features->has_minor_version() && m_features->has_patch_version(), "Invalid Trezor firmware version information"); + return pack_version(m_features->major_version(), m_features->minor_version(), m_features->patch_version()); + } + void set_derivation_path(const std::string &deriv_path) override; + virtual bool has_ki_live_refresh(void) const override { return false; } + + virtual void set_pin(const epee::wipeable_string & pin) override { + m_pin = pin; + } + virtual void set_passphrase(const epee::wipeable_string & passphrase) override { + m_passphrase = passphrase; + } + /* ======================================================================= */ /* SETUP/TEARDOWN */ /* ======================================================================= */ @@ -268,6 +314,23 @@ namespace trezor { void on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg); void on_passphrase_request(GenericMessage & resp, const messages::common::PassphraseRequest * msg); void on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg); + +#ifdef WITH_TREZOR_DEBUGGING + void set_debug(bool debug){ + m_debug = debug; + } + + void set_debug_callback(std::shared_ptr<trezor_debug_callback> & debug_callback){ + m_debug_callback = debug_callback; + } + + void wipe_device(); + void init_device(); + void load_device(const std::string & mnemonic, const std::string & pin="", bool passphrase_protection=false, + const std::string & label="test", const std::string & language="english", + bool skip_checksum=false, bool expand=false); + +#endif }; #endif diff --git a/src/device_trezor/trezor.hpp b/src/device_trezor/trezor.hpp index 97dc0a957..396a27534 100644 --- a/src/device_trezor/trezor.hpp +++ b/src/device_trezor/trezor.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/debug_link.cpp b/src/device_trezor/trezor/debug_link.cpp new file mode 100644 index 000000000..c7ee59afe --- /dev/null +++ b/src/device_trezor/trezor/debug_link.cpp @@ -0,0 +1,90 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "debug_link.hpp" + +namespace hw{ +namespace trezor{ + + DebugLink::DebugLink(){ + + } + + DebugLink::~DebugLink(){ + if (m_transport){ + close(); + } + } + + void DebugLink::init(std::shared_ptr<Transport> & transport){ + CHECK_AND_ASSERT_THROW_MES(!m_transport, "Already initialized"); + m_transport = transport; + m_transport->open(); + } + + void DebugLink::close(){ + CHECK_AND_ASSERT_THROW_MES(m_transport, "Not initialized"); + if (m_transport) m_transport->close(); + } + + std::shared_ptr<messages::debug::DebugLinkState> DebugLink::state(){ + return call<messages::debug::DebugLinkState>( + messages::debug::DebugLinkGetState(), + boost::make_optional(messages::MessageType_DebugLinkGetState)); + } + + void DebugLink::input_word(const std::string & word){ + messages::debug::DebugLinkDecision decision; + decision.set_input(word); + call(decision, boost::none, true); + } + + void DebugLink::input_button(bool button){ + messages::debug::DebugLinkDecision decision; + decision.set_yes_no(button); + call(decision, boost::none, true); + } + + void DebugLink::input_swipe(bool swipe){ + messages::debug::DebugLinkDecision decision; + decision.set_up_down(swipe); + call(decision, boost::none, true); + } + + void DebugLink::stop(){ + messages::debug::DebugLinkStop msg; + call(msg, boost::none, true); + } + + + + + +} +}
\ No newline at end of file diff --git a/src/device_trezor/trezor/debug_link.hpp b/src/device_trezor/trezor/debug_link.hpp new file mode 100644 index 000000000..adf5f1d8f --- /dev/null +++ b/src/device_trezor/trezor/debug_link.hpp @@ -0,0 +1,93 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef MONERO_DEBUG_LINK_H +#define MONERO_DEBUG_LINK_H + +#include "transport.hpp" +#include "messages/messages-debug.pb.h" + + +namespace hw { +namespace trezor { + + class DebugLink { + public: + + DebugLink(); + virtual ~DebugLink(); + + void init(std::shared_ptr<Transport> & transport); + void close(); + + std::shared_ptr<messages::debug::DebugLinkState> state(); + void input_word(const std::string & word); + void input_button(bool button); + void input_swipe(bool swipe); + void press_yes() { input_button(true); } + void press_no() { input_button(false); } + void stop(); + + template<class t_message=messages::debug::DebugLinkState> + std::shared_ptr<t_message> call( + const google::protobuf::Message & req, + const boost::optional<messages::MessageType> &resp_type = boost::none, + bool no_wait = false) + { + BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value); + + m_transport->write(req); + if (no_wait){ + return nullptr; + } + + // Read the response + std::shared_ptr<google::protobuf::Message> msg_resp; + hw::trezor::messages::MessageType msg_resp_type; + m_transport->read(msg_resp, &msg_resp_type); + + messages::MessageType required_type = resp_type ? resp_type.get() : MessageMapper::get_message_wire_number<t_message>(); + if (msg_resp_type == required_type) { + return message_ptr_retype<t_message>(msg_resp); + } else if (msg_resp_type == messages::MessageType_Failure){ + throw_failure_exception(dynamic_cast<messages::common::Failure*>(msg_resp.get())); + } else { + throw exc::UnexpectedMessageException(msg_resp_type, msg_resp); + } + }; + + private: + std::shared_ptr<Transport> m_transport; + + }; + +} +} + +#endif //MONERO_DEBUG_LINK_H diff --git a/src/device_trezor/trezor/exceptions.hpp b/src/device_trezor/trezor/exceptions.hpp index 197dc43a4..41e8c2875 100644 --- a/src/device_trezor/trezor/exceptions.hpp +++ b/src/device_trezor/trezor/exceptions.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/messages_map.cpp b/src/device_trezor/trezor/messages_map.cpp index b0d1aa254..6956b369a 100644 --- a/src/device_trezor/trezor/messages_map.cpp +++ b/src/device_trezor/trezor/messages_map.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -33,6 +33,10 @@ #include "messages/messages-management.pb.h" #include "messages/messages-monero.pb.h" +#ifdef WITH_TREZOR_DEBUGGING +#include "messages/messages-debug.pb.h" +#endif + using namespace std; using namespace hw::trezor; @@ -45,6 +49,9 @@ namespace trezor "hw.trezor.messages.", "hw.trezor.messages.common.", "hw.trezor.messages.management.", +#ifdef WITH_TREZOR_DEBUGGING + "hw.trezor.messages.debug.", +#endif "hw.trezor.messages.monero." }; @@ -68,6 +75,10 @@ namespace trezor hw::trezor::messages::management::Cancel::default_instance(); hw::trezor::messages::monero::MoneroGetAddress::default_instance(); +#ifdef WITH_TREZOR_DEBUGGING + hw::trezor::messages::debug::DebugLinkDecision::default_instance(); +#endif + google::protobuf::Descriptor const * desc = nullptr; for(const string &text : PACKAGES){ desc = google::protobuf::DescriptorPool::generated_pool() diff --git a/src/device_trezor/trezor/messages_map.hpp b/src/device_trezor/trezor/messages_map.hpp index f61338f09..28fe9f4c2 100644 --- a/src/device_trezor/trezor/messages_map.hpp +++ b/src/device_trezor/trezor/messages_map.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -62,14 +62,14 @@ namespace trezor { static messages::MessageType get_message_wire_number(const google::protobuf::Message & msg); static messages::MessageType get_message_wire_number(const std::string & msg_name); - template<class t_message> + template<class t_message=google::protobuf::Message> static messages::MessageType get_message_wire_number() { BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value); return get_message_wire_number(t_message::default_instance().GetDescriptor()->name()); } }; - template<class t_message> + template<class t_message=google::protobuf::Message> std::shared_ptr<t_message> message_ptr_retype(std::shared_ptr<google::protobuf::Message> & in){ BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value); if (!in){ @@ -79,7 +79,7 @@ namespace trezor { return std::dynamic_pointer_cast<t_message>(in); } - template<class t_message> + template<class t_message=google::protobuf::Message> std::shared_ptr<t_message> message_ptr_retype_static(std::shared_ptr<google::protobuf::Message> & in){ BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value); if (!in){ diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp index 13506a67f..db2444df6 100644 --- a/src/device_trezor/trezor/protocol.cpp +++ b/src/device_trezor/trezor/protocol.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -33,6 +33,8 @@ #include <utility> #include <boost/endian/conversion.hpp> #include <common/apply_permutation.h> +#include <common/json_util.h> +#include <crypto/hmac-keccak.h> #include <ringct/rctSigs.h> #include <ringct/bulletproofs.h> #include "cryptonote_config.h" @@ -40,6 +42,37 @@ #include <sodium/crypto_verify_32.h> #include <sodium/crypto_aead_chacha20poly1305.h> +#define GET_FIELD_STRING(name, type, jtype) field_##name = std::string(json[#name].GetString(), json[#name].GetStringLength()) +#define GET_FIELD_OTHER(name, type, jtype) field_##name = static_cast<type>(json[#name].Get##jtype()) + +#define GET_STRING_FROM_JSON(json, name, type, mandatory, def) \ + GET_FIELD_FROM_JSON_EX(json, name, type, String, mandatory, def, GET_FIELD_STRING) + +#define GET_FIELD_FROM_JSON(json, name, type, jtype, mandatory, def) \ + GET_FIELD_FROM_JSON_EX(json, name, type, jtype, mandatory, def, GET_FIELD_OTHER) + +#define GET_FIELD_FROM_JSON_EX(json, name, type, jtype, mandatory, def, VAL) \ + type field_##name = static_cast<type>(def); \ + bool field_##name##_found = false; \ + (void)field_##name##_found; \ + do if (json.HasMember(#name)) \ + { \ + if (json[#name].Is##jtype()) \ + { \ + VAL(name, type, jtype); \ + field_##name##_found = true; \ + } \ + else \ + { \ + throw std::invalid_argument("Field " #name " found in JSON, but not " #jtype); \ + } \ + } \ + else if (mandatory) \ + { \ + throw std::invalid_argument("Field " #name " not found in JSON");\ + } while(0) + + namespace hw{ namespace trezor{ namespace protocol{ @@ -84,19 +117,22 @@ namespace protocol{ namespace crypto { namespace chacha { - void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext){ - if (length < 16){ - throw std::invalid_argument("Ciphertext length too small"); - } + void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext, size_t *plaintext_len){ + CHECK_AND_ASSERT_THROW_MES(length >= TAG_SIZE, "Ciphertext length too small"); + CHECK_AND_ASSERT_THROW_MES(!plaintext_len || *plaintext_len >= (length - TAG_SIZE), "Plaintext length too small"); - unsigned long long int cip_len = length; + unsigned long long int res_len = plaintext_len ? *plaintext_len : length; auto r = crypto_aead_chacha20poly1305_ietf_decrypt( - reinterpret_cast<unsigned char *>(plaintext), &cip_len, nullptr, + reinterpret_cast<unsigned char *>(plaintext), &res_len, nullptr, static_cast<const unsigned char *>(ciphertext), length, nullptr, 0, iv, key); if (r != 0){ throw exc::Poly1305TagInvalid(); } + + if (plaintext_len){ + *plaintext_len = (size_t) res_len; + } } } @@ -185,6 +221,49 @@ namespace ki { } } + void live_refresh_ack(const ::crypto::secret_key & view_key_priv, + const ::crypto::public_key& out_key, + const std::shared_ptr<messages::monero::MoneroLiveRefreshStepAck> & ack, + ::cryptonote::keypair& in_ephemeral, + ::crypto::key_image& ki) + { + std::string str_out_key(out_key.data, sizeof(out_key.data)); + auto enc_key = protocol::tx::compute_enc_key(view_key_priv, str_out_key, ack->salt()); + + const size_t len_ciphertext = ack->key_image().size(); // IV || keys + CHECK_AND_ASSERT_THROW_MES(len_ciphertext > crypto::chacha::IV_SIZE + crypto::chacha::TAG_SIZE, "Invalid size"); + + size_t ki_len = len_ciphertext - crypto::chacha::IV_SIZE - crypto::chacha::TAG_SIZE; + std::unique_ptr<uint8_t[]> plaintext(new uint8_t[ki_len]); + uint8_t * buff = plaintext.get(); + + protocol::crypto::chacha::decrypt( + ack->key_image().data() + crypto::chacha::IV_SIZE, + len_ciphertext - crypto::chacha::IV_SIZE, + reinterpret_cast<const uint8_t *>(enc_key.data), + reinterpret_cast<const uint8_t *>(ack->key_image().data()), + reinterpret_cast<char *>(buff), &ki_len); + + CHECK_AND_ASSERT_THROW_MES(ki_len == 3*32, "Invalid size"); + ::crypto::signature sig{}; + memcpy(ki.data, buff, 32); + memcpy(sig.c.data, buff + 32, 32); + memcpy(sig.r.data, buff + 64, 32); + in_ephemeral.pub = out_key; + in_ephemeral.sec = ::crypto::null_skey; + + // Verification + std::vector<const ::crypto::public_key*> pkeys; + pkeys.push_back(&out_key); + + CHECK_AND_ASSERT_THROW_MES(rct::scalarmultKey(rct::ki2rct(ki), rct::curveOrder()) == rct::identity(), + "Key image out of validity domain: key image " << epee::string_tools::pod_to_hex(ki)); + + CHECK_AND_ASSERT_THROW_MES(::crypto::check_ring_signature((const ::crypto::hash&)ki, ki, pkeys, &sig), + "Signature failed for key image " << epee::string_tools::pod_to_hex(ki) + << ", signature " + epee::string_tools::pod_to_hex(sig) + << ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0])); + } } // Cold transaction signing @@ -198,6 +277,8 @@ namespace tx { void translate_dst_entry(MoneroTransactionDestinationEntry * dst, const cryptonote::tx_destination_entry * src){ dst->set_amount(src->amount); dst->set_is_subaddress(src->is_subaddress); + dst->set_is_integrated(src->is_integrated); + dst->set_original(src->original); translate_address(dst->mutable_addr(), &(src->addr)); } @@ -267,9 +348,29 @@ namespace tx { return std::string(buff, offset); } + ::crypto::secret_key compute_enc_key(const ::crypto::secret_key & private_view_key, const std::string & aux, const std::string & salt) + { + uint8_t hash[32]; + KECCAK_CTX ctx; + ::crypto::secret_key res; + + keccak_init(&ctx); + keccak_update(&ctx, (const uint8_t *) private_view_key.data, sizeof(private_view_key.data)); + if (!aux.empty()){ + keccak_update(&ctx, (const uint8_t *) aux.data(), aux.size()); + } + keccak_finish(&ctx, hash); + keccak(hash, sizeof(hash), hash, sizeof(hash)); + + hmac_keccak_hash(hash, (const uint8_t *) salt.data(), salt.size(), hash, sizeof(hash)); + memcpy(res.data, hash, sizeof(hash)); + memwipe(hash, sizeof(hash)); + return res; + } + TData::TData() { - in_memory = false; rsig_type = 0; + bp_version = 0; cur_input_idx = 0; cur_output_idx = 0; cur_batch_idx = 0; @@ -283,6 +384,7 @@ namespace tx { m_tx_idx = tx_idx; m_ct.tx_data = cur_tx(); m_multisig = false; + m_client_version = 1; } void Signer::extract_payment_id(){ @@ -392,8 +494,10 @@ namespace tx { m_ct.tx.version = 2; m_ct.tx.unlock_time = tx.unlock_time; + m_client_version = (m_aux_data->client_version ? m_aux_data->client_version.get() : 1); tsx_data.set_version(1); + tsx_data.set_client_version(client_version()); tsx_data.set_unlock_time(tx.unlock_time); tsx_data.set_num_inputs(static_cast<google::protobuf::uint32>(tx.sources.size())); tsx_data.set_mixin(static_cast<google::protobuf::uint32>(tx.sources[0].outputs.size() - 1)); @@ -404,6 +508,10 @@ namespace tx { auto rsig_data = tsx_data.mutable_rsig_data(); m_ct.rsig_type = get_rsig_type(tx.use_bulletproofs, tx.splitted_dsts.size()); rsig_data->set_rsig_type(m_ct.rsig_type); + if (tx.use_bulletproofs){ + m_ct.bp_version = (m_aux_data->bp_version ? m_aux_data->bp_version.get() : 1); + rsig_data->set_bp_version((uint32_t) m_ct.bp_version); + } generate_rsig_batch_sizes(m_ct.grouping_vct, m_ct.rsig_type, tx.splitted_dsts.size()); assign_to_repeatable(rsig_data->mutable_grouping(), m_ct.grouping_vct.begin(), m_ct.grouping_vct.end()); @@ -437,7 +545,6 @@ namespace tx { } void Signer::step_init_ack(std::shared_ptr<const messages::monero::MoneroTransactionInitAck> ack){ - m_ct.in_memory = false; if (ack->has_rsig_data()){ m_ct.rsig_param = std::make_shared<MoneroRsigData>(ack->rsig_data()); } @@ -505,10 +612,6 @@ namespace tx { std::shared_ptr<messages::monero::MoneroTransactionInputsPermutationRequest> Signer::step_permutation(){ sort_ki(); - if (in_memory()){ - return nullptr; - } - auto res = std::make_shared<messages::monero::MoneroTransactionInputsPermutationRequest>(); assign_to_repeatable(res->mutable_perm(), m_ct.source_permutation.begin(), m_ct.source_permutation.end()); @@ -516,15 +619,10 @@ namespace tx { } void Signer::step_permutation_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputsPermutationAck> ack){ - if (in_memory()){ - return; - } + } std::shared_ptr<messages::monero::MoneroTransactionInputViniRequest> Signer::step_set_vini_input(size_t idx){ - if (in_memory()){ - return nullptr; - } CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.sources.size(), "Invalid transaction index"); CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx.vin.size(), "Invalid transaction index"); CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_in_hmacs.size(), "Invalid transaction index"); @@ -536,7 +634,8 @@ namespace tx { translate_src_entry(res->mutable_src_entr(), &(tx.sources[idx])); res->set_vini(cryptonote::t_serializable_object_to_blob(vini)); res->set_vini_hmac(m_ct.tx_in_hmacs[idx]); - if (!in_memory()) { + + if (client_version() == 0) { CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index"); CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index"); res->set_pseudo_out(m_ct.pseudo_outs[idx]); @@ -547,9 +646,7 @@ namespace tx { } void Signer::step_set_vini_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputViniAck> ack){ - if (in_memory()){ - return; - } + } std::shared_ptr<messages::monero::MoneroTransactionAllInputsSetRequest> Signer::step_all_inputs_set(){ @@ -557,34 +654,37 @@ namespace tx { } void Signer::step_all_inputs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllInputsSetAck> ack){ - if (is_offloading()){ - // If offloading, expect rsig configuration. - if (!ack->has_rsig_data()){ - throw exc::ProtocolException("Rsig offloading requires rsig param"); - } + if (client_version() > 0 || !is_offloading()){ + return; + } - auto & rsig_data = ack->rsig_data(); - if (!rsig_data.has_mask()){ - throw exc::ProtocolException("Gamma masks not present in offloaded version"); - } + // If offloading, expect rsig configuration. + if (!ack->has_rsig_data()){ + throw exc::ProtocolException("Rsig offloading requires rsig param"); + } - auto & mask = rsig_data.mask(); - if (mask.size() != 32 * num_outputs()){ - throw exc::ProtocolException("Invalid number of gamma masks"); - } + auto & rsig_data = ack->rsig_data(); + if (!rsig_data.has_mask()){ + throw exc::ProtocolException("Gamma masks not present in offloaded version"); + } - m_ct.rsig_gamma.reserve(num_outputs()); - for(size_t c=0; c < num_outputs(); ++c){ - rct::key cmask{}; - memcpy(cmask.bytes, mask.data() + c * 32, 32); - m_ct.rsig_gamma.emplace_back(cmask); - } + auto & mask = rsig_data.mask(); + if (mask.size() != 32 * num_outputs()){ + throw exc::ProtocolException("Invalid number of gamma masks"); + } + + m_ct.rsig_gamma.reserve(num_outputs()); + for(size_t c=0; c < num_outputs(); ++c){ + rct::key cmask{}; + memcpy(cmask.bytes, mask.data() + c * 32, 32); + m_ct.rsig_gamma.emplace_back(cmask); } } std::shared_ptr<messages::monero::MoneroTransactionSetOutputRequest> Signer::step_set_output(size_t idx){ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.splitted_dsts.size(), "Invalid transaction index"); CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_out_entr_hmacs.size(), "Invalid transaction index"); + CHECK_AND_ASSERT_THROW_MES(is_req_bulletproof(), "Borromean rsig not supported"); m_ct.cur_output_idx = idx; m_ct.cur_output_in_batch_idx += 1; // assumes sequential call to step_set_output() @@ -595,48 +695,11 @@ namespace tx { res->set_dst_entr_hmac(m_ct.tx_out_entr_hmacs[idx]); // Range sig offloading to the host - if (!is_offloading()) { - return res; - } - - CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index"); - if (m_ct.grouping_vct[m_ct.cur_batch_idx] > m_ct.cur_output_in_batch_idx) { - return res; - } - - auto rsig_data = res->mutable_rsig_data(); - auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx]; - - if (!is_req_bulletproof()){ - if (batch_size > 1){ - throw std::invalid_argument("Borromean cannot batch outputs"); - } - - CHECK_AND_ASSERT_THROW_MES(idx < m_ct.rsig_gamma.size(), "Invalid gamma index"); - rct::key C{}, mask = m_ct.rsig_gamma[idx]; - auto genRsig = rct::proveRange(C, mask, cur_dst.amount); // TODO: rsig with given mask - auto serRsig = cn_serialize(genRsig); - m_ct.tx_out_rsigs.emplace_back(genRsig); - rsig_data->set_rsig(serRsig); - - } else { - std::vector<uint64_t> amounts; - rct::keyV masks; - CHECK_AND_ASSERT_THROW_MES(idx + 1 >= batch_size, "Invalid index for batching"); - - for(size_t i = 0; i < batch_size; ++i){ - const size_t bidx = 1 + idx - batch_size + i; - CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_data.splitted_dsts.size(), "Invalid gamma index"); - CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.rsig_gamma.size(), "Invalid gamma index"); - - amounts.push_back(m_ct.tx_data.splitted_dsts[bidx].amount); - masks.push_back(m_ct.rsig_gamma[bidx]); - } - - auto bp = bulletproof_PROVE(amounts, masks); - auto serRsig = cn_serialize(bp); - m_ct.tx_out_rsigs.emplace_back(bp); - rsig_data->set_rsig(serRsig); + // ClientV0 sends offloaded BP with the last message in the batch. + // ClientV1 needs additional message after the last message in the batch as BP uses deterministic masks. + if (client_version() == 0 && is_offloading() && should_compute_bp_now()) { + auto rsig_data = res->mutable_rsig_data(); + compute_bproof(*rsig_data); } return res; @@ -644,7 +707,6 @@ namespace tx { void Signer::step_set_output_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetOutputAck> ack){ cryptonote::tx_out tx_out; - rct::rangeSig range_sig{}; rct::Bulletproof bproof{}; rct::ctkey out_pk{}; rct::ecdhTuple ecdh{}; @@ -658,12 +720,12 @@ namespace tx { if (rsig_data.has_rsig() && !rsig_data.rsig().empty()){ has_rsig = true; rsig_buff = rsig_data.rsig(); + } - } else if (rsig_data.rsig_parts_size() > 0){ - has_rsig = true; - for (const auto &it : rsig_data.rsig_parts()) { - rsig_buff += it; - } + if (client_version() >= 1 && rsig_data.has_mask()){ + rct::key cmask{}; + string_to_key(cmask, rsig_data.mask()); + m_ct.rsig_gamma.emplace_back(cmask); } } @@ -675,12 +737,13 @@ namespace tx { throw exc::ProtocolException("Cannot deserialize out_pk"); } - if (!cn_deserialize(ack->ecdh_info(), ecdh)){ - throw exc::ProtocolException("Cannot deserialize ecdhtuple"); - } - - if (has_rsig && !is_req_bulletproof() && !cn_deserialize(rsig_buff, range_sig)){ - throw exc::ProtocolException("Cannot deserialize rangesig"); + if (m_ct.bp_version <= 1) { + if (!cn_deserialize(ack->ecdh_info(), ecdh)){ + throw exc::ProtocolException("Cannot deserialize ecdhtuple"); + } + } else { + CHECK_AND_ASSERT_THROW_MES(8 == ack->ecdh_info().size(), "Invalid ECDH.amount size"); + memcpy(ecdh.amount.bytes, ack->ecdh_info().data(), 8); } if (has_rsig && is_req_bulletproof() && !cn_deserialize(rsig_buff, bproof)){ @@ -692,35 +755,77 @@ namespace tx { m_ct.tx_out_pk.emplace_back(out_pk); m_ct.tx_out_ecdh.emplace_back(ecdh); - if (!has_rsig){ + // ClientV0, if no rsig was generated on Trezor, do not continue. + // ClientV1+ generates BP after all masks in the current batch are generated + if (!has_rsig || (client_version() >= 1 && is_offloading())){ return; } - if (is_req_bulletproof()){ - CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index"); - auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx]; - for (size_t i = 0; i < batch_size; ++i){ - const size_t bidx = 1 + m_ct.cur_output_idx - batch_size + i; - CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_out_pk.size(), "Invalid out index"); + process_bproof(bproof); + m_ct.cur_batch_idx += 1; + m_ct.cur_output_in_batch_idx = 0; + } - rct::key commitment = m_ct.tx_out_pk[bidx].mask; - commitment = rct::scalarmultKey(commitment, rct::INV_EIGHT); - bproof.V.push_back(commitment); - } + bool Signer::should_compute_bp_now() const { + CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index"); + return m_ct.grouping_vct[m_ct.cur_batch_idx] <= m_ct.cur_output_in_batch_idx; + } - m_ct.tx_out_rsigs.emplace_back(bproof); - if (!rct::bulletproof_VERIFY(boost::get<rct::Bulletproof>(m_ct.tx_out_rsigs.back()))) { - throw exc::ProtocolException("Returned range signature is invalid"); - } + void Signer::compute_bproof(messages::monero::MoneroTransactionRsigData & rsig_data){ + auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx]; + std::vector<uint64_t> amounts; + rct::keyV masks; + CHECK_AND_ASSERT_THROW_MES(m_ct.cur_output_idx + 1 >= batch_size, "Invalid index for batching"); - } else { - m_ct.tx_out_rsigs.emplace_back(range_sig); + for(size_t i = 0; i < batch_size; ++i){ + const size_t bidx = 1 + m_ct.cur_output_idx - batch_size + i; + CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_data.splitted_dsts.size(), "Invalid gamma index"); + CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.rsig_gamma.size(), "Invalid gamma index"); - if (!rct::verRange(out_pk.mask, boost::get<rct::rangeSig>(m_ct.tx_out_rsigs.back()))) { - throw exc::ProtocolException("Returned range signature is invalid"); - } + amounts.push_back(m_ct.tx_data.splitted_dsts[bidx].amount); + masks.push_back(m_ct.rsig_gamma[bidx]); } + auto bp = bulletproof_PROVE(amounts, masks); + auto serRsig = cn_serialize(bp); + m_ct.tx_out_rsigs.emplace_back(bp); + rsig_data.set_rsig(serRsig); + } + + void Signer::process_bproof(rct::Bulletproof & bproof){ + CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index"); + auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx]; + for (size_t i = 0; i < batch_size; ++i){ + const size_t bidx = 1 + m_ct.cur_output_idx - batch_size + i; + CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_out_pk.size(), "Invalid out index"); + + rct::key commitment = m_ct.tx_out_pk[bidx].mask; + commitment = rct::scalarmultKey(commitment, rct::INV_EIGHT); + bproof.V.push_back(commitment); + } + + m_ct.tx_out_rsigs.emplace_back(bproof); + if (!rct::bulletproof_VERIFY(boost::get<rct::Bulletproof>(m_ct.tx_out_rsigs.back()))) { + throw exc::ProtocolException("Returned range signature is invalid"); + } + } + + std::shared_ptr<messages::monero::MoneroTransactionSetOutputRequest> Signer::step_rsig(size_t idx){ + if (client_version() == 0 || !is_offloading() || !should_compute_bp_now()){ + return nullptr; + } + + auto res = std::make_shared<messages::monero::MoneroTransactionSetOutputRequest>(); + auto & cur_dst = m_ct.tx_data.splitted_dsts[idx]; + translate_dst_entry(res->mutable_dst_entr(), &cur_dst); + res->set_dst_entr_hmac(m_ct.tx_out_entr_hmacs[idx]); + + compute_bproof(*(res->mutable_rsig_data())); + res->set_is_offloaded_bp(true); + return res; + } + + void Signer::step_set_rsig_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetOutputAck> ack){ m_ct.cur_batch_idx += 1; m_ct.cur_output_in_batch_idx = 0; } @@ -814,12 +919,11 @@ namespace tx { res->set_vini_hmac(m_ct.tx_in_hmacs[idx]); res->set_pseudo_out_alpha(m_ct.alphas[idx]); res->set_spend_key(m_ct.spend_encs[idx]); - if (!in_memory()){ - CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index"); - CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index"); - res->set_pseudo_out(m_ct.pseudo_outs[idx]); - res->set_pseudo_out_hmac(m_ct.pseudo_outs_hmac[idx]); - } + + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index"); + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index"); + res->set_pseudo_out(m_ct.pseudo_outs[idx]); + res->set_pseudo_out_hmac(m_ct.pseudo_outs_hmac[idx]); return res; } @@ -829,6 +933,19 @@ namespace tx { throw exc::ProtocolException("Cannot deserialize mg[i]"); } + // Sync updated pseudo_outputs, client_version>=1, HF10+ + if (client_version() >= 1 && ack->has_pseudo_out()){ + CHECK_AND_ASSERT_THROW_MES(m_ct.cur_input_idx < m_ct.pseudo_outs.size(), "Invalid pseudo-out index"); + m_ct.pseudo_outs[m_ct.cur_input_idx] = ack->pseudo_out(); + if (is_bulletproof()){ + CHECK_AND_ASSERT_THROW_MES(m_ct.cur_input_idx < m_ct.rv->p.pseudoOuts.size(), "Invalid pseudo-out index"); + string_to_key(m_ct.rv->p.pseudoOuts[m_ct.cur_input_idx], ack->pseudo_out()); + } else { + CHECK_AND_ASSERT_THROW_MES(m_ct.cur_input_idx < m_ct.rv->pseudoOuts.size(), "Invalid pseudo-out index"); + string_to_key(m_ct.rv->pseudoOuts[m_ct.cur_input_idx], ack->pseudo_out()); + } + } + m_ct.rv->p.MGs.push_back(mg); } @@ -841,14 +958,14 @@ namespace tx { if (m_multisig){ auto & cout_key = ack->cout_key(); for(auto & cur : m_ct.couts){ - if (cur.size() != 12 + 32){ + if (cur.size() != crypto::chacha::IV_SIZE + 32){ throw std::invalid_argument("Encrypted cout has invalid length"); } char buff[32]; auto data = cur.data(); - crypto::chacha::decrypt(data + 12, 32, reinterpret_cast<const uint8_t *>(cout_key.data()), reinterpret_cast<const uint8_t *>(data), buff); + crypto::chacha::decrypt(data + crypto::chacha::IV_SIZE, 32, reinterpret_cast<const uint8_t *>(cout_key.data()), reinterpret_cast<const uint8_t *>(data), buff); m_ct.couts_dec.emplace_back(buff, 32); } } @@ -887,6 +1004,82 @@ namespace tx { return sb.GetString(); } + void load_tx_key_data(hw::device_cold::tx_key_data_t & res, const std::string & data) + { + rapidjson::Document json; + + // The contents should be JSON if the wallet follows the new format. + if (json.Parse(data.c_str()).HasParseError()) + { + throw std::invalid_argument("Data parsing error"); + } + else if(!json.IsObject()) + { + throw std::invalid_argument("Data parsing error - not an object"); + } + + GET_FIELD_FROM_JSON(json, version, int, Int, true, -1); + GET_STRING_FROM_JSON(json, salt1, std::string, true, std::string()); + GET_STRING_FROM_JSON(json, salt2, std::string, true, std::string()); + GET_STRING_FROM_JSON(json, enc_keys, std::string, true, std::string()); + GET_STRING_FROM_JSON(json, tx_prefix_hash, std::string, false, std::string()); + + if (field_version != 1) + { + throw std::invalid_argument("Unknown version"); + } + + res.salt1 = field_salt1; + res.salt2 = field_salt2; + res.tx_enc_keys = field_enc_keys; + res.tx_prefix_hash = field_tx_prefix_hash; + } + + std::shared_ptr<messages::monero::MoneroGetTxKeyRequest> get_tx_key( + const hw::device_cold::tx_key_data_t & tx_data) + { + auto req = std::make_shared<messages::monero::MoneroGetTxKeyRequest>(); + req->set_salt1(tx_data.salt1); + req->set_salt2(tx_data.salt2); + req->set_tx_enc_keys(tx_data.tx_enc_keys); + req->set_tx_prefix_hash(tx_data.tx_prefix_hash); + req->set_reason(0); + + return req; + } + + void get_tx_key_ack( + std::vector<::crypto::secret_key> & tx_keys, + const std::string & tx_prefix_hash, + const ::crypto::secret_key & view_key_priv, + std::shared_ptr<const messages::monero::MoneroGetTxKeyAck> ack + ) + { + auto enc_key = protocol::tx::compute_enc_key(view_key_priv, tx_prefix_hash, ack->salt()); + auto & encrypted_keys = ack->has_tx_derivations() ? ack->tx_derivations() : ack->tx_keys(); + + const size_t len_ciphertext = encrypted_keys.size(); // IV || keys || TAG + CHECK_AND_ASSERT_THROW_MES(len_ciphertext > crypto::chacha::IV_SIZE + crypto::chacha::TAG_SIZE, "Invalid size"); + + size_t keys_len = len_ciphertext - crypto::chacha::IV_SIZE - crypto::chacha::TAG_SIZE; + std::unique_ptr<uint8_t[]> plaintext(new uint8_t[keys_len]); + + protocol::crypto::chacha::decrypt( + encrypted_keys.data() + crypto::chacha::IV_SIZE, + len_ciphertext - crypto::chacha::IV_SIZE, + reinterpret_cast<const uint8_t *>(enc_key.data), + reinterpret_cast<const uint8_t *>(encrypted_keys.data()), + reinterpret_cast<char *>(plaintext.get()), &keys_len); + + CHECK_AND_ASSERT_THROW_MES(keys_len % 32 == 0, "Invalid size"); + tx_keys.resize(keys_len / 32); + + for(unsigned i = 0; i < keys_len / 32; ++i) + { + memcpy(tx_keys[i].data, plaintext.get() + 32 * i, 32); + } + memwipe(plaintext.get(), keys_len); + } } } diff --git a/src/device_trezor/trezor/protocol.hpp b/src/device_trezor/trezor/protocol.hpp index ce0361640..98ca52565 100644 --- a/src/device_trezor/trezor/protocol.hpp +++ b/src/device_trezor/trezor/protocol.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -92,11 +92,14 @@ namespace protocol{ // Crypto / encryption namespace crypto { namespace chacha { + // Constants as defined in RFC 7539. + const unsigned IV_SIZE = 12; + const unsigned TAG_SIZE = 16; // crypto_aead_chacha20poly1305_IETF_ABYTES; /** * Chacha20Poly1305 decryption with tag verification. RFC 7539. */ - void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext); + void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext, size_t *plaintext_len=nullptr); } } @@ -129,6 +132,14 @@ namespace ki { const std::vector<tools::wallet2::transfer_details> & transfers, std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> & req); + /** + * Processes Live refresh step response, parses KI, checks the signature + */ + void live_refresh_ack(const ::crypto::secret_key & view_key_priv, + const ::crypto::public_key& out_key, + const std::shared_ptr<messages::monero::MoneroLiveRefreshStepAck> & ack, + ::cryptonote::keypair& in_ephemeral, + ::crypto::key_image& ki); } // Cold transaction signing @@ -153,6 +164,7 @@ namespace tx { std::string hash_addr(const MoneroAccountPublicAddress * addr, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none); std::string hash_addr(const std::string & spend_key, const std::string & view_key, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none); std::string hash_addr(const ::crypto::public_key * spend_key, const ::crypto::public_key * view_key, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none); + ::crypto::secret_key compute_enc_key(const ::crypto::secret_key & private_view_key, const std::string & aux, const std::string & salt); typedef boost::variant<rct::rangeSig, rct::Bulletproof> rsig_v; @@ -164,8 +176,8 @@ namespace tx { TsxData tsx_data; tx_construction_data tx_data; cryptonote::transaction tx; - bool in_memory; unsigned rsig_type; + int bp_version; std::vector<uint64_t> grouping_vct; std::shared_ptr<MoneroRsigData> rsig_param; size_t cur_input_idx; @@ -206,6 +218,7 @@ namespace tx { const unsigned_tx_set * m_unsigned_tx; hw::tx_aux_data * m_aux_data; + unsigned m_client_version; bool m_multisig; const tx_construction_data & cur_tx(){ @@ -215,6 +228,9 @@ namespace tx { void extract_payment_id(); void compute_integrated_indices(TsxData * tsx_data); + bool should_compute_bp_now() const; + void compute_bproof(messages::monero::MoneroTransactionRsigData & rsig_data); + void process_bproof(rct::Bulletproof & bproof); public: Signer(wallet_shim * wallet2, const unsigned_tx_set * unsigned_tx, size_t tx_idx = 0, hw::tx_aux_data * aux_data = nullptr); @@ -238,6 +254,9 @@ namespace tx { std::shared_ptr<messages::monero::MoneroTransactionSetOutputRequest> step_set_output(size_t idx); void step_set_output_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetOutputAck> ack); + std::shared_ptr<messages::monero::MoneroTransactionSetOutputRequest> step_rsig(size_t idx); + void step_set_rsig_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetOutputAck> ack); + std::shared_ptr<messages::monero::MoneroTransactionAllOutSetRequest> step_all_outs_set(); void step_all_outs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllOutSetAck> ack, hw::device &hwdev); @@ -249,8 +268,8 @@ namespace tx { std::string store_tx_aux_info(); - bool in_memory() const { - return m_ct.in_memory; + unsigned client_version() const { + return m_client_version; } bool is_simple() const { @@ -290,6 +309,18 @@ namespace tx { } }; + // TX Key decryption + void load_tx_key_data(hw::device_cold::tx_key_data_t & res, const std::string & data); + + std::shared_ptr<messages::monero::MoneroGetTxKeyRequest> get_tx_key( + const hw::device_cold::tx_key_data_t & tx_data); + + void get_tx_key_ack( + std::vector<::crypto::secret_key> & tx_keys, + const std::string & tx_prefix_hash, + const ::crypto::secret_key & view_key_priv, + std::shared_ptr<const messages::monero::MoneroGetTxKeyAck> ack + ); } } diff --git a/src/device_trezor/trezor/tools/build_protob.py b/src/device_trezor/trezor/tools/build_protob.py index 2611f3296..eb32f6b4d 100644 --- a/src/device_trezor/trezor/tools/build_protob.py +++ b/src/device_trezor/trezor/tools/build_protob.py @@ -2,6 +2,12 @@ import os import subprocess import sys +import argparse + + +parser = argparse.ArgumentParser() +parser.add_argument("-d", "--debug-msg", default=False, action="store_const", const=True, help="Build debug messages") +args = parser.parse_args() CWD = os.path.dirname(os.path.realpath(__file__)) ROOT_DIR = os.path.abspath(os.path.join(CWD, "..", "..", "..", "..")) @@ -24,6 +30,10 @@ try: "messages-management.proto", "messages-monero.proto", ] + + if args.debug_msg: + selected += ["messages-debug.proto"] + proto_srcs = [os.path.join(TREZOR_COMMON, "protob", x) for x in selected] exec_args = [ sys.executable, diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp index cd66e59e8..991ba3395 100644 --- a/src/device_trezor/trezor/transport.cpp +++ b/src/device_trezor/trezor/transport.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -84,6 +84,17 @@ namespace trezor{ return std::string(in.GetString()); } + uint64_t pack_version(uint32_t major, uint32_t minor, uint32_t patch) + { + // packing (major, minor, patch) to 64 B: 16 B | 24 B | 24 B + const unsigned bits_1 = 16; + const unsigned bits_2 = 24; + const uint32_t mask_1 = (1 << bits_1) - 1; + const uint32_t mask_2 = (1 << bits_2) - 1; + CHECK_AND_ASSERT_THROW_MES(major <= mask_1 && minor <= mask_2 && patch <= mask_2, "Version numbers overflow packing scheme"); + return patch | (((uint64_t)minor) << bits_2) | (((uint64_t)major) << (bits_1 + bits_2)); + } + // // Helpers // @@ -212,6 +223,40 @@ namespace trezor{ msg = msg_wrap; } + Transport::Transport(): m_open_counter(0) { + + } + + bool Transport::pre_open(){ + if (m_open_counter > 0){ + MTRACE("Already opened, count: " << m_open_counter); + m_open_counter += 1; + return false; + + } else if (m_open_counter < 0){ + MTRACE("Negative open value: " << m_open_counter); + + } + + // Caller should set m_open_counter to 1 after open + m_open_counter = 0; + return true; + } + + bool Transport::pre_close(){ + m_open_counter -= 1; + + if (m_open_counter < 0){ + MDEBUG("Already closed. Counter " << m_open_counter); + + } else if (m_open_counter == 0) { + return true; + + } + + return false; + } + // // Bridge transport // @@ -246,6 +291,10 @@ namespace trezor{ } void BridgeTransport::open() { + if (!pre_open()){ + return; + } + if (!m_device_path){ throw exc::CommunicationException("Coud not open, empty device path"); } @@ -259,9 +308,15 @@ namespace trezor{ } m_session = boost::make_optional(json_get_string(bridge_res["session"])); + m_open_counter = 1; } void BridgeTransport::close() { + if (!pre_close()){ + return; + } + + MTRACE("Closing Trezor:BridgeTransport"); if (!m_device_path || !m_session){ throw exc::CommunicationException("Device not open"); } @@ -423,6 +478,10 @@ namespace trezor{ } void UdpTransport::open() { + if (!pre_open()){ + return; + } + udp::resolver resolver(m_io_service); udp::resolver::query query(udp::v4(), m_device_host, std::to_string(m_device_port)); m_endpoint = *resolver.resolve(query); @@ -434,10 +493,16 @@ namespace trezor{ check_deadline(); m_proto->session_begin(*this); + m_open_counter = 1; } void UdpTransport::close() { - if (!m_socket){ + if (!pre_close()){ + return; + } + + MTRACE("Closing Trezor:UdpTransport"); + if (!m_socket) { throw exc::CommunicationException("Socket is already closed"); } @@ -446,6 +511,19 @@ namespace trezor{ m_socket = nullptr; } + std::shared_ptr<Transport> UdpTransport::find_debug() { +#ifdef WITH_TREZOR_DEBUGGING + std::shared_ptr<UdpTransport> t = std::make_shared<UdpTransport>(); + t->m_proto = std::make_shared<ProtocolV1>(); + t->m_device_host = m_device_host; + t->m_device_port = m_device_port + 1; + return t; +#else + MINFO("Debug link is disabled in production"); + return nullptr; +#endif + } + void UdpTransport::write_chunk(const void * buff, size_t size){ require_socket(); @@ -660,8 +738,7 @@ namespace trezor{ WebUsbTransport::WebUsbTransport( boost::optional<libusb_device_descriptor*> descriptor, boost::optional<std::shared_ptr<Protocol>> proto - ): m_conn_count(0), - m_usb_session(nullptr), m_usb_device(nullptr), m_usb_device_handle(nullptr), + ): m_usb_session(nullptr), m_usb_device(nullptr), m_usb_device_handle(nullptr), m_bus_id(-1), m_device_addr(-1) { if (descriptor){ @@ -672,7 +749,7 @@ namespace trezor{ m_proto = proto ? proto.get() : std::make_shared<ProtocolV1>(); -#ifdef WITH_TREZOR_DEBUG +#ifdef WITH_TREZOR_DEBUGGING m_debug_mode = false; #endif } @@ -757,12 +834,10 @@ namespace trezor{ }; void WebUsbTransport::open() { - const int interface = get_interface(); - if (m_conn_count > 0){ - MTRACE("Already opened, count: " << m_conn_count); - m_conn_count += 1; + if (!pre_open()){ return; } + const int interface = get_interface(); #define TREZOR_DESTROY_SESSION() do { libusb_exit(m_usb_session); m_usb_session = nullptr; } while(0) @@ -840,45 +915,55 @@ namespace trezor{ throw exc::DeviceAcquireException("Unable to claim libusb device"); } - m_conn_count = 1; + m_open_counter = 1; m_proto->session_begin(*this); #undef TREZOR_DESTROY_SESSION }; void WebUsbTransport::close() { - m_conn_count -= 1; - - if (m_conn_count < 0){ - MERROR("Close counter is negative: " << m_conn_count); - - } else if (m_conn_count == 0){ - MTRACE("Closing webusb device"); + if (!pre_close()){ + return; + } - m_proto->session_end(*this); + MTRACE("Closing Trezor:WebUsbTransport"); + m_proto->session_end(*this); - int r = libusb_release_interface(m_usb_device_handle, get_interface()); - if (r != 0){ - MERROR("Could not release libusb interface: " << r); - } + int r = libusb_release_interface(m_usb_device_handle, get_interface()); + if (r != 0){ + MERROR("Could not release libusb interface: " << r); + } - m_usb_device = nullptr; - if (m_usb_device_handle) { - libusb_close(m_usb_device_handle); - m_usb_device_handle = nullptr; - } + m_usb_device = nullptr; + if (m_usb_device_handle) { + libusb_close(m_usb_device_handle); + m_usb_device_handle = nullptr; + } - if (m_usb_session) { - libusb_exit(m_usb_session); - m_usb_session = nullptr; - } + if (m_usb_session) { + libusb_exit(m_usb_session); + m_usb_session = nullptr; } }; + std::shared_ptr<Transport> WebUsbTransport::find_debug() { +#ifdef WITH_TREZOR_DEBUGGING + require_device(); + auto t = std::make_shared<WebUsbTransport>(boost::make_optional(m_usb_device_desc.get())); + t->m_bus_id = m_bus_id; + t->m_device_addr = m_device_addr; + t->m_port_numbers = m_port_numbers; + t->m_debug_mode = true; + return t; +#else + MINFO("Debug link is disabled in production"); + return nullptr; +#endif + } int WebUsbTransport::get_interface() const{ const int INTERFACE_NORMAL = 0; -#ifdef WITH_TREZOR_DEBUG +#ifdef WITH_TREZOR_DEBUGGING const int INTERFACE_DEBUG = 1; return m_debug_mode ? INTERFACE_DEBUG : INTERFACE_NORMAL; #else @@ -888,7 +973,7 @@ namespace trezor{ unsigned char WebUsbTransport::get_endpoint() const{ const unsigned char ENDPOINT_NORMAL = 1; -#ifdef WITH_TREZOR_DEBUG +#ifdef WITH_TREZOR_DEBUGGING const unsigned char ENDPOINT_DEBUG = 2; return m_debug_mode ? ENDPOINT_DEBUG : ENDPOINT_NORMAL; #else @@ -1047,4 +1132,3 @@ namespace trezor{ } } - diff --git a/src/device_trezor/trezor/transport.hpp b/src/device_trezor/trezor/transport.hpp index 1cf0daa85..2945b3184 100644 --- a/src/device_trezor/trezor/transport.hpp +++ b/src/device_trezor/trezor/transport.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -62,6 +62,8 @@ namespace trezor { const std::string DEFAULT_BRIDGE = "127.0.0.1:21325"; + uint64_t pack_version(uint32_t major, uint32_t minor=0, uint32_t patch=0); + // Base HTTP comm serialization. bool t_serialize(const std::string & in, std::string & out); bool t_serialize(const json_val & in, std::string & out); @@ -134,7 +136,7 @@ namespace trezor { class Transport { public: - Transport() = default; + Transport(); virtual ~Transport() = default; virtual bool ping() { return false; }; @@ -144,10 +146,16 @@ namespace trezor { virtual void close(){}; virtual void write(const google::protobuf::Message & req) =0; virtual void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) =0; + virtual std::shared_ptr<Transport> find_debug() { return nullptr; }; virtual void write_chunk(const void * buff, size_t size) { }; virtual size_t read_chunk(void * buff, size_t size) { return 0; }; virtual std::ostream& dump(std::ostream& o) const { return o << "Transport<>"; } + protected: + long m_open_counter; + + virtual bool pre_open(); + virtual bool pre_close(); }; // Bridge transport @@ -212,6 +220,7 @@ namespace trezor { void open() override; void close() override; + std::shared_ptr<Transport> find_debug() override; void write(const google::protobuf::Message &req) override; void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override; @@ -259,6 +268,7 @@ namespace trezor { void open() override; void close() override; + std::shared_ptr<Transport> find_debug() override; void write(const google::protobuf::Message &req) override; void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override; @@ -274,7 +284,6 @@ namespace trezor { int get_interface() const; unsigned char get_endpoint() const; - int m_conn_count; std::shared_ptr<Protocol> m_proto; libusb_context *m_usb_session; @@ -285,7 +294,7 @@ namespace trezor { int m_bus_id; int m_device_addr; -#ifdef WITH_TREZOR_DEBUG +#ifdef WITH_TREZOR_DEBUGGING bool m_debug_mode; #endif }; @@ -309,7 +318,7 @@ namespace trezor { /** * Transforms path to the particular transport */ - template<class t_transport> + template<class t_transport=Transport> std::shared_ptr<t_transport> transport_typed(const std::string & path){ auto t = transport(path); if (!t){ @@ -362,7 +371,7 @@ namespace trezor { * @throws UnexpectedMessageException if the response message type is different than expected. * Exception contains message type and the message itself. */ - template<class t_message> + template<class t_message=google::protobuf::Message> std::shared_ptr<t_message> exchange_message(Transport & transport, const google::protobuf::Message & req, boost::optional<messages::MessageType> resp_type = boost::none) diff --git a/src/device_trezor/trezor/trezor_defs.hpp b/src/device_trezor/trezor/trezor_defs.hpp index 30e76eadc..f0697cdb5 100644 --- a/src/device_trezor/trezor/trezor_defs.hpp +++ b/src/device_trezor/trezor/trezor_defs.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // |