35 #ifdef WITH_DEVICE_TREZOR 37 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY 38 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "device.trezor" 40 #define HW_TREZOR_NAME "Trezor" 42 static device_trezor *trezor_device =
nullptr;
43 static device_trezor *ensure_trezor_device(){
45 trezor_device =
new device_trezor();
52 registry.insert(std::make_pair(
HW_TREZOR_NAME, std::unique_ptr<device>(ensure_trezor_device())));
59 device_trezor::device_trezor() {
60 m_live_refresh_in_progress =
false;
61 m_live_refresh_enabled =
true;
62 m_live_refresh_thread_running =
false;
65 device_trezor::~device_trezor() {
69 }
catch(std::exception
const& e){
70 MWARNING(
"Could not disconnect and release: " << e.what());
76 m_live_refresh_in_progress =
false;
78 if (r && !m_live_refresh_thread)
80 m_live_refresh_thread_running =
true;
81 m_live_refresh_thread.reset(
new boost::thread(boost::bind(&device_trezor::live_refresh_thread_main,
this)));
86 bool device_trezor::release()
88 m_live_refresh_in_progress =
false;
89 m_live_refresh_thread_running =
false;
90 if (m_live_refresh_thread)
92 m_live_refresh_thread->join();
93 m_live_refresh_thread =
nullptr;
95 return device_trezor_base::release();
98 bool device_trezor::disconnect()
100 m_live_refresh_in_progress =
false;
101 return device_trezor_base::disconnect();
104 void device_trezor::device_state_reset_unsafe()
107 if (m_live_refresh_in_progress)
111 live_refresh_finish_unsafe();
113 catch(
const std::exception & e)
115 MERROR(
"Live refresh could not be terminated: " << e.what());
119 m_live_refresh_in_progress =
false;
120 device_trezor_base::device_state_reset_unsafe();
123 void device_trezor::live_refresh_thread_main()
125 while(m_live_refresh_thread_running)
127 boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
128 if (!m_live_refresh_in_progress)
134 if (!m_transport || !m_live_refresh_in_progress)
139 auto current_time = std::chrono::steady_clock::now();
140 if (current_time - m_last_live_refresh_time <= std::chrono::minutes(5))
145 MTRACE(
"Closing live refresh process due to inactivity");
148 live_refresh_finish();
150 catch(
const std::exception &e)
152 MWARNING(
"Live refresh auto-finish failed: " << e.what());
170 pubkey =
info.address;
173 }
catch(std::exception
const& e){
174 MERROR(
"Get public address exception: " << e.what());
181 MDEBUG(
"Loading view-only key from the Trezor. Please check the Trezor for a confirmation.");
182 auto res = get_view_key();
189 #ifndef WITH_TREZOR_DEBUGGING 193 memcpy(viewkey.data,
res->watch_key().data(), 32);
197 }
catch(std::exception
const& e){
198 MERROR(
"Get secret keys exception: " << e.what());
212 const boost::optional<std::vector<uint32_t>> & path,
213 const boost::optional<cryptonote::network_type> &
network_type){
216 device_state_reset_unsafe();
217 require_initialized();
219 auto req = std::make_shared<messages::Electroneum::ElectroneumGetAddress>();
220 this->set_msg_addr<messages::Electroneum::ElectroneumGetAddress>(req.get(), path,
network_type);
222 auto response = this->client_exchange<messages::Electroneum::ElectroneumAddress>(req);
223 MTRACE(
"Get address response received");
227 std::shared_ptr<messages::Electroneum::ElectroneumWatchKey> device_trezor::get_view_key(
228 const boost::optional<std::vector<uint32_t>> & path,
229 const boost::optional<cryptonote::network_type> &
network_type){
232 device_state_reset_unsafe();
233 require_initialized();
235 auto req = std::make_shared<messages::Electroneum::ElectroneumGetWatchKey>();
236 this->set_msg_addr<messages::Electroneum::ElectroneumGetWatchKey>(req.get(), path,
network_type);
238 auto response = this->client_exchange<messages::Electroneum::ElectroneumWatchKey>(req);
239 MTRACE(
"Get watch key response received");
243 bool device_trezor::is_get_tx_key_supported()
const 245 require_initialized();
255 std::vector<::crypto::secret_key> & tx_keys,
256 const ::hw::device_cold::tx_key_data_t &
tx_aux_data,
261 device_state_reset_unsafe();
262 require_initialized();
265 this->set_msg_addr<messages::Electroneum::ElectroneumGetTxKeyRequest>(req.get());
267 auto response = this->client_exchange<messages::Electroneum::ElectroneumGetTxKeyAck>(req);
268 MTRACE(
"Get TX key response received");
274 const std::vector<tools::wallet2::transfer_details> & transfers,
277 #define EVENT_PROGRESS(P) do { if (m_callback) {(m_callback)->on_progress(device_cold::op_progress(P)); } }while(0) 281 device_state_reset_unsafe();
282 require_initialized();
284 std::shared_ptr<messages::Electroneum::ElectroneumKeyImageExportInitRequest> req;
286 std::vector<protocol::ki::ElectroneumTransferDetails> mtds;
287 std::vector<protocol::ki::ElectroneumExportedKeyImage> kis;
292 this->set_msg_addr<messages::Electroneum::ElectroneumKeyImageExportInitRequest>(req.get());
293 auto ack1 = this->client_exchange<messages::Electroneum::ElectroneumKeyImageExportInitAck>(req);
295 const auto batch_size = 10;
296 const auto num_batches = (mtds.size() + batch_size - 1) / batch_size;
297 for(
uint64_t cur = 0; cur < num_batches; ++cur){
298 auto step_req = std::make_shared<messages::Electroneum::ElectroneumKeyImageSyncStepRequest>();
299 auto idx_finish = std::min(static_cast<uint64_t>((cur + 1) * batch_size), static_cast<uint64_t>(mtds.size()));
300 for(
uint64_t idx = cur * batch_size; idx < idx_finish; ++idx){
301 auto added_tdis = step_req->add_tdis();
303 *added_tdis = mtds[idx];
306 auto step_ack = this->client_exchange<messages::Electroneum::ElectroneumKeyImageSyncStepAck>(step_req);
307 auto kis_size = step_ack->kis_size();
308 kis.reserve(static_cast<size_t>(kis_size));
309 for(
int i = 0; i < kis_size; ++i){
310 auto ckis = step_ack->kis(i);
314 MTRACE(
"Batch " << cur <<
" / " << num_batches <<
" batches processed");
315 EVENT_PROGRESS((
double)cur * batch_size / mtds.size());
319 auto final_req = std::make_shared<messages::Electroneum::ElectroneumKeyImageSyncFinalRequest>();
320 auto final_ack = this->client_exchange<messages::Electroneum::ElectroneumKeyImageSyncFinalAck>(final_req);
321 ski.reserve(kis.size());
323 for(
auto & sub : kis){
326 char buff[
sizeof(ki.data)*3];
328 size_t buff_len =
sizeof(buff);
331 reinterpret_cast<const uint8_t *
>(final_ack->enc_key().data()),
332 reinterpret_cast<const uint8_t *>(sub.iv().data()), buff, &buff_len);
335 memcpy(ki.data, buff,
sizeof(ki.data));
336 memcpy(sig.c.data, buff +
sizeof(ki.data),
sizeof(ki.data));
337 memcpy(sig.r.data, buff + 2*
sizeof(ki.data),
sizeof(ki.data));
338 ski.push_back(std::make_pair(ki, sig));
340 #undef EVENT_PROGRESS 343 bool device_trezor::is_live_refresh_supported()
const 345 require_initialized();
349 bool device_trezor::is_live_refresh_enabled()
const 351 return is_live_refresh_supported() && (mode == NONE || mode == TRANSACTION_PARSE) && m_live_refresh_enabled;
354 bool device_trezor::has_ki_live_refresh()
const 357 return is_live_refresh_enabled();
358 }
catch(
const std::exception & e){
359 MERROR(
"Could not detect if live refresh is enabled: " << e.what());
364 void device_trezor::live_refresh_start()
368 live_refresh_start_unsafe();
371 void device_trezor::live_refresh_start_unsafe()
373 device_state_reset_unsafe();
374 require_initialized();
376 auto req = std::make_shared<messages::Electroneum::ElectroneumLiveRefreshStartRequest>();
377 this->set_msg_addr<messages::Electroneum::ElectroneumLiveRefreshStartRequest>(req.get());
378 this->client_exchange<messages::Electroneum::ElectroneumLiveRefreshStartAck>(req);
379 m_live_refresh_in_progress =
true;
380 m_last_live_refresh_time = std::chrono::steady_clock::now();
383 void device_trezor::live_refresh(
387 size_t real_output_index,
396 if (!m_live_refresh_in_progress)
398 live_refresh_start_unsafe();
401 m_last_live_refresh_time = std::chrono::steady_clock::now();
403 auto req = std::make_shared<messages::Electroneum::ElectroneumLiveRefreshStepRequest>();
404 req->set_out_key(out_key.data, 32);
405 req->set_recv_deriv(recv_derivation.data, 32);
406 req->set_real_out_idx(real_output_index);
407 req->set_sub_addr_major(received_index.
major);
408 req->set_sub_addr_minor(received_index.
minor);
410 auto ack = this->client_exchange<messages::Electroneum::ElectroneumLiveRefreshStepAck>(req);
414 void device_trezor::live_refresh_finish_unsafe()
416 auto req = std::make_shared<messages::Electroneum::ElectroneumLiveRefreshFinalRequest>();
417 this->client_exchange<messages::Electroneum::ElectroneumLiveRefreshFinalAck>(req);
418 m_live_refresh_in_progress =
false;
421 void device_trezor::live_refresh_finish()
425 if (m_live_refresh_in_progress)
427 live_refresh_finish_unsafe();
431 void device_trezor::computing_key_images(
bool started)
435 if (!is_live_refresh_enabled())
441 if (!started && m_live_refresh_in_progress)
443 live_refresh_finish();
446 catch(
const std::exception & e)
448 MWARNING(
"KI computation state change failed, started: " << started <<
", e: " << e.what());
452 bool device_trezor::compute_key_image(
453 const ::cryptonote::account_keys& ack,
456 size_t real_output_index,
457 const ::cryptonote::subaddress_index& received_index,
461 if (!is_live_refresh_enabled())
466 live_refresh(ack.m_view_secret_key, out_key, recv_derivation, real_output_index, received_index, in_ephemeral, ki);
479 device_state_reset_unsafe();
480 require_initialized();
481 transaction_versions_check(unsigned_tx, aux_data);
483 const size_t num_tx = unsigned_tx.
txes.size();
484 m_num_transations_to_sign = num_tx;
488 for(
size_t tx_idx = 0; tx_idx < num_tx; ++tx_idx) {
489 std::shared_ptr<protocol::tx::Signer> signer;
490 tx_sign(wallet, unsigned_tx, tx_idx, aux_data, signer);
492 auto & cdata = signer->tdata();
493 auto aux_info_cur = signer->store_tx_aux_info();
497 signed_tx.
ptx.emplace_back();
498 auto & cpend = signed_tx.
ptx.back();
502 cpend.dust_added_to_fee =
false;
503 cpend.change_dts = cdata.tx_data.change_dts;
504 cpend.selected_transfers = cdata.tx_data.selected_transfers;
505 cpend.key_images =
"";
506 cpend.dests = cdata.tx_data.dests;
507 cpend.construction_data = cdata.tx_data;
511 transaction_check(cdata, aux_data);
512 }
catch(
const std::exception &e){
517 bool all_are_txin_to_key = std::all_of(cdata.tx.vin.begin(), cdata.tx.vin.end(), [&](
const cryptonote::txin_v& s_e) ->
bool 523 if(!all_are_txin_to_key) {
524 throw std::invalid_argument(
"Not all are txin_to_key");
526 cpend.key_images = key_images;
529 for(
size_t cidx=0, trans_max=unsigned_tx.
transfers.second.size(); cidx < trans_max; ++cidx){
533 size_t num_sources = cdata.tx_data.sources.size();
536 for(
size_t src_idx = 0; src_idx < num_sources; ++src_idx){
537 size_t idx_mapped = cdata.source_permutation[src_idx];
541 size_t idx_map_src = cdata.tx_data.selected_transfers[idx_mapped];
544 idx_map_src -= unsigned_tx.
transfers.first;
547 const auto vini = boost::get<cryptonote::txin_to_key>(cdata.tx.vin[src_idx]);
548 signed_tx.
key_images[idx_map_src] = vini.k_image;
553 m_callback->on_progress(
device_cold::tx_progress(m_num_transations_to_sign, m_num_transations_to_sign, 1, 1, 1, 1));
561 std::shared_ptr<protocol::tx::Signer> & signer)
563 #define EVENT_PROGRESS(S, SUB, SUBMAX) do { if (m_callback) { \ 564 (m_callback)->on_progress(device_cold::tx_progress(idx, m_num_transations_to_sign, S, 10, SUB, SUBMAX)); \ 569 device_state_reset_unsafe();
571 require_initialized();
572 EVENT_PROGRESS(0, 1, 1);
575 signer = std::make_shared<protocol::tx::Signer>(wallet, &unsigned_tx, idx, &aux_data);
577 unsigned long num_sources = cur_tx.
sources.size();
578 unsigned long num_outputs = cur_tx.splitted_dsts.size();
581 auto init_msg = signer->step_init();
582 this->set_msg_addr(init_msg.get());
583 transaction_pre_check(init_msg);
584 EVENT_PROGRESS(1, 1, 1);
586 auto response = this->client_exchange<messages::Electroneum::ElectroneumTransactionInitAck>(init_msg);
590 for(
size_t cur_src = 0; cur_src < num_sources; ++cur_src){
591 auto src = signer->step_set_input(cur_src);
592 auto ack = this->client_exchange<messages::Electroneum::ElectroneumTransactionSetInputAck>(src);
593 signer->step_set_input_ack(ack);
594 EVENT_PROGRESS(2, cur_src, num_sources);
598 auto perm_req = signer->step_permutation();
600 auto perm_ack = this->client_exchange<messages::Electroneum::ElectroneumTransactionInputsPermutationAck>(perm_req);
601 signer->step_permutation_ack(perm_ack);
603 EVENT_PROGRESS(3, 1, 1);
606 for(
size_t cur_src = 0; cur_src < num_sources; ++cur_src){
607 auto src = signer->step_set_vini_input(cur_src);
608 auto ack = this->client_exchange<messages::Electroneum::ElectroneumTransactionInputViniAck>(src);
609 signer->step_set_vini_input_ack(ack);
610 EVENT_PROGRESS(4, cur_src, num_sources);
614 auto all_inputs_set = signer->step_all_inputs_set();
615 auto ack_all_inputs = this->client_exchange<messages::Electroneum::ElectroneumTransactionAllInputsSetAck>(all_inputs_set);
616 signer->step_all_inputs_set_ack(ack_all_inputs);
617 EVENT_PROGRESS(5, 1, 1);
620 for(
size_t cur_dst = 0; cur_dst < num_outputs; ++cur_dst){
621 auto src = signer->step_set_output(cur_dst);
622 auto ack = this->client_exchange<messages::Electroneum::ElectroneumTransactionSetOutputAck>(src);
623 signer->step_set_output_ack(ack);
626 auto offloaded_bp = signer->step_rsig(cur_dst);
628 auto bp_ack = this->client_exchange<messages::Electroneum::ElectroneumTransactionSetOutputAck>(offloaded_bp);
629 signer->step_set_rsig_ack(ack);
632 EVENT_PROGRESS(6, cur_dst, num_outputs);
636 auto all_out_set = signer->step_all_outs_set();
637 auto ack_all_out_set = this->client_exchange<messages::Electroneum::ElectroneumTransactionAllOutSetAck>(all_out_set);
638 signer->step_all_outs_set_ack(ack_all_out_set, *
this);
639 EVENT_PROGRESS(7, 1, 1);
642 for(
size_t cur_src = 0; cur_src < num_sources; ++cur_src){
643 auto src = signer->step_sign_input(cur_src);
644 auto ack_sign = this->client_exchange<messages::Electroneum::ElectroneumTransactionSignInputAck>(src);
645 signer->step_sign_input_ack(ack_sign);
646 EVENT_PROGRESS(8, cur_src, num_sources);
650 auto final_msg = signer->step_final();
651 auto ack_final = this->client_exchange<messages::Electroneum::ElectroneumTransactionFinalAck>(final_msg);
652 signer->step_final_ack(ack_final);
653 EVENT_PROGRESS(9, 1, 1);
654 #undef EVENT_PROGRESS 659 auto trezor_version = get_version();
660 unsigned client_version = 1;
668 if (wanted_client_version > client_version){
669 throw exc::TrezorException(
"Trezor firmware 2.0.10 and lower does not support current transaction sign protocol. Please update.");
671 client_version = wanted_client_version;
677 throw exc::TrezorException(
"Trezor firmware 2.0.10 and lower does not support current transaction sign protocol (BPv2+). Please update.");
681 void device_trezor::transaction_pre_check(std::shared_ptr<messages::Electroneum::ElectroneumTransactionInitRequest> init_msg)
686 const bool nonce_required = init_msg->tsx_data().has_payment_id() && init_msg->tsx_data().payment_id().size() > 0;
688 if (nonce_required && init_msg->tsx_data().payment_id().size() == 8){
691 throw exc::TrezorException(
"Trezor firmware 2.0.9 and lower does not support transactions with short payment IDs or integrated addresses. Please update.");
707 std::vector<cryptonote::tx_extra_field> tx_extra_fields;
713 const bool nonce_required = tdata.
tsx_data.has_payment_id() && tdata.
tsx_data.payment_id().size() > 0;
719 if (payment_id.size() == 32){
723 }
else if (payment_id.size() == 8){
730 #else //WITH_DEVICE_TREZOR 738 #endif //WITH_DEVICE_TREZOR
boost::optional< unsigned > client_version
void generate_commitment(std::vector< ElectroneumTransferDetails > &mtds, const std::vector< tools::wallet2::transfer_details > &transfers, std::shared_ptr< messages::Electroneum::ElectroneumKeyImageExportInitRequest > &req)
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
void register_all(std::map< std::string, std::unique_ptr< device >> ®istry)
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::Electroneum::ElectroneumGetTxKeyAck > ack)
cryptonote::transaction tx
epee::misc_utils::struct_init< response_t > response
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
std::vector< std::string > tx_device_aux
boost::variant< txin_gen, txin_to_script, txin_to_scripthash, txin_to_key, txin_to_key_public > txin_v
epee::mlocked< tools::scrubbed< ec_scalar > > secret_key
bool register_device(const std::string &device_name, device *hw_device)
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
std::vector< uint8_t > extra
tools::wallet2::unsigned_tx_set unsigned_tx_set
const crypto::secret_key null_skey
void load_tx_key_data(hw::device_cold::tx_key_data_t &res, const std::string &data)
unsigned __int64 uint64_t
bool t_serializable_object_to_blob(const t_object &to, blobdata &b_blob)
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
bool parse_and_validate_tx_from_blob(const blobdata &tx_blob, transaction &tx)
cryptonote::account_public_address get_address(const var_addr_t &inp)
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
uint64_t pack_version(uint32_t major, uint32_t minor, uint32_t patch)
void decrypt(const void *ciphertext, size_t length, const uint8_t *key, const uint8_t *iv, char *plaintext, size_t *plaintext_len)
void live_refresh_ack(const ::crypto::secret_key &view_key_priv, const ::crypto::public_key &out_key, const std::shared_ptr< messages::Electroneum::ElectroneumLiveRefreshStepAck > &ack, ::cryptonote::keypair &in_ephemeral, ::crypto::key_image &ki)
void * memcpy(void *a, const void *b, size_t c)
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
boost::optional< int > bp_version
bool key_image_data(wallet_shim *wallet, const std::vector< tools::wallet2::transfer_details > &transfers, std::vector< ElectroneumTransferDetails > &res)
std::string to_string(t_connection_type type)
#define TREZOR_AUTO_LOCK_CMD()
#define TREZOR_AUTO_LOCK_DEVICE()
std::vector< std::pair< crypto::key_image, crypto::signature > > exported_key_image