diff options
Diffstat (limited to 'src/device_trezor/trezor')
-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 |
11 files changed, 697 insertions, 176 deletions
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. // |