Electroneum
device_trezor.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017-Present, Electroneum
2 //
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification, are
6 // permitted provided that the following conditions are met:
7 //
8 // 1. Redistributions of source code must retain the above copyright notice, this list of
9 // conditions and the following disclaimer.
10 //
11 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 // of conditions and the following disclaimer in the documentation and/or other
13 // materials provided with the distribution.
14 //
15 // 3. Neither the name of the copyright holder nor the names of its contributors may be
16 // used to endorse or promote products derived from this software without specific
17 // prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29 
30 #include "device_trezor.hpp"
31 
32 namespace hw {
33 namespace trezor {
34 
35 #ifdef WITH_DEVICE_TREZOR
36 
37 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
38 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "device.trezor"
39 
40 #define HW_TREZOR_NAME "Trezor"
41 
42  static device_trezor *trezor_device = nullptr;
43  static device_trezor *ensure_trezor_device(){
44  if (!trezor_device) {
45  trezor_device = new device_trezor();
46  trezor_device->set_name(HW_TREZOR_NAME);
47  }
48  return trezor_device;
49  }
50 
51  void register_all(std::map<std::string, std::unique_ptr<device>> &registry) {
52  registry.insert(std::make_pair(HW_TREZOR_NAME, std::unique_ptr<device>(ensure_trezor_device())));
53  }
54 
55  void register_all() {
56  hw::register_device(HW_TREZOR_NAME, ensure_trezor_device());
57  }
58 
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;
63  }
64 
65  device_trezor::~device_trezor() {
66  try {
67  disconnect();
68  release();
69  } catch(std::exception const& e){
70  MWARNING("Could not disconnect and release: " << e.what());
71  }
72  }
73 
74  bool device_trezor::init()
75  {
76  m_live_refresh_in_progress = false;
77  bool r = device_trezor_base::init();
78  if (r && !m_live_refresh_thread)
79  {
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)));
82  }
83  return r;
84  }
85 
86  bool device_trezor::release()
87  {
88  m_live_refresh_in_progress = false;
89  m_live_refresh_thread_running = false;
90  if (m_live_refresh_thread)
91  {
92  m_live_refresh_thread->join();
93  m_live_refresh_thread = nullptr;
94  }
95  return device_trezor_base::release();
96  }
97 
98  bool device_trezor::disconnect()
99  {
100  m_live_refresh_in_progress = false;
101  return device_trezor_base::disconnect();
102  }
103 
104  void device_trezor::device_state_reset_unsafe()
105  {
106  require_connected();
107  if (m_live_refresh_in_progress)
108  {
109  try
110  {
111  live_refresh_finish_unsafe();
112  }
113  catch(const std::exception & e)
114  {
115  MERROR("Live refresh could not be terminated: " << e.what());
116  }
117  }
118 
119  m_live_refresh_in_progress = false;
120  device_trezor_base::device_state_reset_unsafe();
121  }
122 
123  void device_trezor::live_refresh_thread_main()
124  {
125  while(m_live_refresh_thread_running)
126  {
127  boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
128  if (!m_live_refresh_in_progress)
129  {
130  continue;
131  }
132 
134  if (!m_transport || !m_live_refresh_in_progress)
135  {
136  continue;
137  }
138 
139  auto current_time = std::chrono::steady_clock::now();
140  if (current_time - m_last_live_refresh_time <= std::chrono::minutes(5))
141  {
142  continue;
143  }
144 
145  MTRACE("Closing live refresh process due to inactivity");
146  try
147  {
148  live_refresh_finish();
149  }
150  catch(const std::exception &e)
151  {
152  MWARNING("Live refresh auto-finish failed: " << e.what());
153  }
154  }
155  }
156 
157  /* ======================================================================= */
158  /* WALLET & ADDRESS */
159  /* ======================================================================= */
160 
161  bool device_trezor::get_public_address(cryptonote::account_public_address &pubkey) {
162  try {
163  auto res = get_address();
164 
166  bool r = cryptonote::get_account_address_from_str(info, this->network_type, res->address());
167  CHECK_AND_ASSERT_MES(r, false, "Could not parse returned address. Address parse failed: " + res->address());
168  CHECK_AND_ASSERT_MES(!info.is_subaddress, false, "Trezor returned a sub address");
169 
170  pubkey = info.address;
171  return true;
172 
173  } catch(std::exception const& e){
174  MERROR("Get public address exception: " << e.what());
175  return false;
176  }
177  }
178 
179  bool device_trezor::get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) {
180  try {
181  MDEBUG("Loading view-only key from the Trezor. Please check the Trezor for a confirmation.");
182  auto res = get_view_key();
183  CHECK_AND_ASSERT_MES(res->watch_key().size() == 32, false, "Trezor returned invalid view key");
184 
185  // Trezor does not make use of spendkey of the device API.
186  // Ledger loads encrypted spendkey, Trezor loads null key (never leaves device).
187  // In the test (debugging mode) we need to leave this field intact as it is already set by
188  // the debugging code and need to remain same for the testing purposes.
189 #ifndef WITH_TREZOR_DEBUGGING
190  spendkey = crypto::null_skey; // not given
191 #endif
192 
193  memcpy(viewkey.data, res->watch_key().data(), 32);
194 
195  return true;
196 
197  } catch(std::exception const& e){
198  MERROR("Get secret keys exception: " << e.what());
199  return false;
200  }
201  }
202 
203  /* ======================================================================= */
204  /* Helpers */
205  /* ======================================================================= */
206 
207  /* ======================================================================= */
208  /* TREZOR PROTOCOL */
209  /* ======================================================================= */
210 
211  std::shared_ptr<messages::Electroneum::ElectroneumAddress> device_trezor::get_address(
212  const boost::optional<std::vector<uint32_t>> & path,
213  const boost::optional<cryptonote::network_type> & network_type){
215  require_connected();
216  device_state_reset_unsafe();
217  require_initialized();
218 
219  auto req = std::make_shared<messages::Electroneum::ElectroneumGetAddress>();
220  this->set_msg_addr<messages::Electroneum::ElectroneumGetAddress>(req.get(), path, network_type);
221 
222  auto response = this->client_exchange<messages::Electroneum::ElectroneumAddress>(req);
223  MTRACE("Get address response received");
224  return response;
225  }
226 
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){
231  require_connected();
232  device_state_reset_unsafe();
233  require_initialized();
234 
235  auto req = std::make_shared<messages::Electroneum::ElectroneumGetWatchKey>();
236  this->set_msg_addr<messages::Electroneum::ElectroneumGetWatchKey>(req.get(), path, network_type);
237 
238  auto response = this->client_exchange<messages::Electroneum::ElectroneumWatchKey>(req);
239  MTRACE("Get watch key response received");
240  return response;
241  }
242 
243  bool device_trezor::is_get_tx_key_supported() const
244  {
245  require_initialized();
246  return get_version() > pack_version(2, 0, 10);
247  }
248 
250  {
252  }
253 
255  std::vector<::crypto::secret_key> & tx_keys,
256  const ::hw::device_cold::tx_key_data_t & tx_aux_data,
257  const ::crypto::secret_key & view_key_priv)
258  {
260  require_connected();
261  device_state_reset_unsafe();
262  require_initialized();
263 
265  this->set_msg_addr<messages::Electroneum::ElectroneumGetTxKeyRequest>(req.get());
266 
267  auto response = this->client_exchange<messages::Electroneum::ElectroneumGetTxKeyAck>(req);
268  MTRACE("Get TX key response received");
269 
270  protocol::tx::get_tx_key_ack(tx_keys, tx_aux_data.tx_prefix_hash, view_key_priv, response);
271  }
272 
273  void device_trezor::ki_sync(wallet_shim * wallet,
274  const std::vector<tools::wallet2::transfer_details> & transfers,
276  {
277 #define EVENT_PROGRESS(P) do { if (m_callback) {(m_callback)->on_progress(device_cold::op_progress(P)); } }while(0)
278 
280  require_connected();
281  device_state_reset_unsafe();
282  require_initialized();
283 
284  std::shared_ptr<messages::Electroneum::ElectroneumKeyImageExportInitRequest> req;
285 
286  std::vector<protocol::ki::ElectroneumTransferDetails> mtds;
287  std::vector<protocol::ki::ElectroneumExportedKeyImage> kis;
288  protocol::ki::key_image_data(wallet, transfers, mtds);
289  protocol::ki::generate_commitment(mtds, transfers, req);
290 
291  EVENT_PROGRESS(0.);
292  this->set_msg_addr<messages::Electroneum::ElectroneumKeyImageExportInitRequest>(req.get());
293  auto ack1 = this->client_exchange<messages::Electroneum::ElectroneumKeyImageExportInitAck>(req);
294 
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();
302  CHECK_AND_ASSERT_THROW_MES(idx < mtds.size(), "Invalid transfer detail index");
303  *added_tdis = mtds[idx];
304  }
305 
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);
311  kis.push_back(ckis);
312  }
313 
314  MTRACE("Batch " << cur << " / " << num_batches << " batches processed");
315  EVENT_PROGRESS((double)cur * batch_size / mtds.size());
316  }
317  EVENT_PROGRESS(1.);
318 
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());
322 
323  for(auto & sub : kis){
324  ::crypto::signature sig{};
326  char buff[sizeof(ki.data)*3];
327 
328  size_t buff_len = sizeof(buff);
329 
330  protocol::crypto::chacha::decrypt(sub.blob().data(), sub.blob().size(),
331  reinterpret_cast<const uint8_t *>(final_ack->enc_key().data()),
332  reinterpret_cast<const uint8_t *>(sub.iv().data()), buff, &buff_len);
333  CHECK_AND_ASSERT_THROW_MES(buff_len == sizeof(buff), "Plaintext size invalid");
334 
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));
339  }
340 #undef EVENT_PROGRESS
341  }
342 
343  bool device_trezor::is_live_refresh_supported() const
344  {
345  require_initialized();
346  return get_version() > pack_version(2, 0, 10);
347  }
348 
349  bool device_trezor::is_live_refresh_enabled() const
350  {
351  return is_live_refresh_supported() && (mode == NONE || mode == TRANSACTION_PARSE) && m_live_refresh_enabled;
352  }
353 
354  bool device_trezor::has_ki_live_refresh() const
355  {
356  try{
357  return is_live_refresh_enabled();
358  } catch(const std::exception & e){
359  MERROR("Could not detect if live refresh is enabled: " << e.what());
360  }
361  return false;
362  }
363 
364  void device_trezor::live_refresh_start()
365  {
367  require_connected();
368  live_refresh_start_unsafe();
369  }
370 
371  void device_trezor::live_refresh_start_unsafe()
372  {
373  device_state_reset_unsafe();
374  require_initialized();
375 
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();
381  }
382 
383  void device_trezor::live_refresh(
384  const ::crypto::secret_key & view_key_priv,
385  const crypto::public_key& out_key,
386  const crypto::key_derivation& recv_derivation,
387  size_t real_output_index,
388  const cryptonote::subaddress_index& received_index,
389  cryptonote::keypair& in_ephemeral,
391  )
392  {
394  require_connected();
395 
396  if (!m_live_refresh_in_progress)
397  {
398  live_refresh_start_unsafe();
399  }
400 
401  m_last_live_refresh_time = std::chrono::steady_clock::now();
402 
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);
409 
410  auto ack = this->client_exchange<messages::Electroneum::ElectroneumLiveRefreshStepAck>(req);
411  protocol::ki::live_refresh_ack(view_key_priv, out_key, ack, in_ephemeral, ki);
412  }
413 
414  void device_trezor::live_refresh_finish_unsafe()
415  {
416  auto req = std::make_shared<messages::Electroneum::ElectroneumLiveRefreshFinalRequest>();
417  this->client_exchange<messages::Electroneum::ElectroneumLiveRefreshFinalAck>(req);
418  m_live_refresh_in_progress = false;
419  }
420 
421  void device_trezor::live_refresh_finish()
422  {
424  require_connected();
425  if (m_live_refresh_in_progress)
426  {
427  live_refresh_finish_unsafe();
428  }
429  }
430 
431  void device_trezor::computing_key_images(bool started)
432  {
433  try
434  {
435  if (!is_live_refresh_enabled())
436  {
437  return;
438  }
439 
440  // React only on termination as the process can auto-start itself.
441  if (!started && m_live_refresh_in_progress)
442  {
443  live_refresh_finish();
444  }
445  }
446  catch(const std::exception & e)
447  {
448  MWARNING("KI computation state change failed, started: " << started << ", e: " << e.what());
449  }
450  }
451 
452  bool device_trezor::compute_key_image(
453  const ::cryptonote::account_keys& ack,
455  const ::crypto::key_derivation& recv_derivation,
456  size_t real_output_index,
457  const ::cryptonote::subaddress_index& received_index,
458  ::cryptonote::keypair& in_ephemeral,
459  ::crypto::key_image& ki)
460  {
461  if (!is_live_refresh_enabled())
462  {
463  return false;
464  }
465 
466  live_refresh(ack.m_view_secret_key, out_key, recv_derivation, real_output_index, received_index, in_ephemeral, ki);
467  return true;
468  }
469 
470  void device_trezor::tx_sign(wallet_shim * wallet,
471  const tools::wallet2::unsigned_tx_set & unsigned_tx,
472  tools::wallet2::signed_tx_set & signed_tx,
473  hw::tx_aux_data & aux_data)
474  {
475  CHECK_AND_ASSERT_THROW_MES(unsigned_tx.transfers.first == 0, "Unsuported non zero offset");
476 
478  require_connected();
479  device_state_reset_unsafe();
480  require_initialized();
481  transaction_versions_check(unsigned_tx, aux_data);
482 
483  const size_t num_tx = unsigned_tx.txes.size();
484  m_num_transations_to_sign = num_tx;
485  signed_tx.key_images.clear();
486  signed_tx.key_images.resize(unsigned_tx.transfers.second.size());
487 
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);
491 
492  auto & cdata = signer->tdata();
493  auto aux_info_cur = signer->store_tx_aux_info();
494  aux_data.tx_device_aux.emplace_back(aux_info_cur);
495 
496  // Pending tx reconstruction
497  signed_tx.ptx.emplace_back();
498  auto & cpend = signed_tx.ptx.back();
499  cpend.tx = cdata.tx;
500  cpend.dust = 0;
501  cpend.fee = 0;
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;
508 
509  // Transaction check
510  try {
511  transaction_check(cdata, aux_data);
512  } catch(const std::exception &e){
513  throw exc::ProtocolException(std::string("Transaction verification failed: ") + e.what());
514  }
515 
516  std::string key_images;
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
518  {
520  key_images += boost::to_string(in.k_image) + " ";
521  return true;
522  });
523  if(!all_are_txin_to_key) {
524  throw std::invalid_argument("Not all are txin_to_key");
525  }
526  cpend.key_images = key_images;
527 
528  // KI sync
529  for(size_t cidx=0, trans_max=unsigned_tx.transfers.second.size(); cidx < trans_max; ++cidx){
530  signed_tx.key_images[cidx] = unsigned_tx.transfers.second[cidx].m_key_image;
531  }
532 
533  size_t num_sources = cdata.tx_data.sources.size();
534  CHECK_AND_ASSERT_THROW_MES(num_sources == cdata.source_permutation.size(), "Invalid permutation size");
535  CHECK_AND_ASSERT_THROW_MES(num_sources == cdata.tx.vin.size(), "Invalid tx.vin size");
536  for(size_t src_idx = 0; src_idx < num_sources; ++src_idx){
537  size_t idx_mapped = cdata.source_permutation[src_idx];
538  CHECK_AND_ASSERT_THROW_MES(idx_mapped < cdata.tx_data.selected_transfers.size(), "Invalid idx_mapped");
539  CHECK_AND_ASSERT_THROW_MES(src_idx < cdata.tx.vin.size(), "Invalid idx_mapped");
540 
541  size_t idx_map_src = cdata.tx_data.selected_transfers[idx_mapped];
542  CHECK_AND_ASSERT_THROW_MES(idx_map_src >= unsigned_tx.transfers.first, "Invalid offset");
543 
544  idx_map_src -= unsigned_tx.transfers.first;
545  CHECK_AND_ASSERT_THROW_MES(idx_map_src < signed_tx.key_images.size(), "Invalid key image index");
546 
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;
549  }
550  }
551 
552  if (m_callback){
553  m_callback->on_progress(device_cold::tx_progress(m_num_transations_to_sign, m_num_transations_to_sign, 1, 1, 1, 1));
554  }
555  }
556 
557  void device_trezor::tx_sign(wallet_shim * wallet,
558  const tools::wallet2::unsigned_tx_set & unsigned_tx,
559  size_t idx,
560  hw::tx_aux_data & aux_data,
561  std::shared_ptr<protocol::tx::Signer> & signer)
562  {
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)); \
565 } }while(0)
566 
567  require_connected();
568  if (idx > 0)
569  device_state_reset_unsafe();
570 
571  require_initialized();
572  EVENT_PROGRESS(0, 1, 1);
573 
574  CHECK_AND_ASSERT_THROW_MES(idx < unsigned_tx.txes.size(), "Invalid transaction index");
575  signer = std::make_shared<protocol::tx::Signer>(wallet, &unsigned_tx, idx, &aux_data);
576  const tools::wallet2::tx_construction_data & cur_tx = unsigned_tx.txes[idx];
577  unsigned long num_sources = cur_tx.sources.size();
578  unsigned long num_outputs = cur_tx.splitted_dsts.size();
579 
580  // Step: Init
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);
585 
586  auto response = this->client_exchange<messages::Electroneum::ElectroneumTransactionInitAck>(init_msg);
587  signer->step_init_ack(response);
588 
589  // Step: Set transaction inputs
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);
595  }
596 
597  // Step: sort
598  auto perm_req = signer->step_permutation();
599  if (perm_req){
600  auto perm_ack = this->client_exchange<messages::Electroneum::ElectroneumTransactionInputsPermutationAck>(perm_req);
601  signer->step_permutation_ack(perm_ack);
602  }
603  EVENT_PROGRESS(3, 1, 1);
604 
605  // Step: input_vini
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);
611  }
612 
613  // Step: all inputs set
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);
618 
619  // Step: outputs
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);
624 
625  // If BP is offloaded to host, another step with computed BP may be needed.
626  auto offloaded_bp = signer->step_rsig(cur_dst);
627  if (offloaded_bp){
628  auto bp_ack = this->client_exchange<messages::Electroneum::ElectroneumTransactionSetOutputAck>(offloaded_bp);
629  signer->step_set_rsig_ack(ack);
630  }
631 
632  EVENT_PROGRESS(6, cur_dst, num_outputs);
633  }
634 
635  // Step: all outs set
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);
640 
641  // Step: sign each input
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);
647  }
648 
649  // Step: final
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
655  }
656 
657  void device_trezor::transaction_versions_check(const ::tools::wallet2::unsigned_tx_set & unsigned_tx, hw::tx_aux_data & aux_data)
658  {
659  auto trezor_version = get_version();
660  unsigned client_version = 1; // default client version for tx
661 
662  if (trezor_version <= pack_version(2, 0, 10)){
663  client_version = 0;
664  }
665 
666  if (aux_data.client_version){
667  auto wanted_client_version = aux_data.client_version.get();
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.");
670  } else {
671  client_version = wanted_client_version;
672  }
673  }
674  aux_data.client_version = client_version;
675 
676  if (client_version == 0 && aux_data.bp_version && aux_data.bp_version.get() != 1){
677  throw exc::TrezorException("Trezor firmware 2.0.10 and lower does not support current transaction sign protocol (BPv2+). Please update.");
678  }
679  }
680 
681  void device_trezor::transaction_pre_check(std::shared_ptr<messages::Electroneum::ElectroneumTransactionInitRequest> init_msg)
682  {
683  CHECK_AND_ASSERT_THROW_MES(init_msg, "TransactionInitRequest is empty");
684  CHECK_AND_ASSERT_THROW_MES(init_msg->has_tsx_data(), "TransactionInitRequest has no transaction data");
685  CHECK_AND_ASSERT_THROW_MES(m_features, "Device state not initialized"); // make sure the caller did not reset features
686  const bool nonce_required = init_msg->tsx_data().has_payment_id() && init_msg->tsx_data().payment_id().size() > 0;
687 
688  if (nonce_required && init_msg->tsx_data().payment_id().size() == 8){
689  // Versions 2.0.9 and lower do not support payment ID
690  if (get_version() <= pack_version(2, 0, 9)) {
691  throw exc::TrezorException("Trezor firmware 2.0.9 and lower does not support transactions with short payment IDs or integrated addresses. Please update.");
692  }
693  }
694  }
695 
696  void device_trezor::transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data)
697  {
698  // Simple serialization check
699  cryptonote::blobdata tx_blob;
700  cryptonote::transaction tx_deserialized;
701  bool r = cryptonote::t_serializable_object_to_blob(tdata.tx, tx_blob);
702  CHECK_AND_ASSERT_THROW_MES(r, "Transaction serialization failed");
703  r = cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx_deserialized);
704  CHECK_AND_ASSERT_THROW_MES(r, "Transaction deserialization failed");
705 
706  // Extras check
707  std::vector<cryptonote::tx_extra_field> tx_extra_fields;
709 
710  r = cryptonote::parse_tx_extra(tdata.tx.extra, tx_extra_fields);
711  CHECK_AND_ASSERT_THROW_MES(r, "tx.extra parsing failed");
712 
713  const bool nonce_required = tdata.tsx_data.has_payment_id() && tdata.tsx_data.payment_id().size() > 0;
714  const bool has_nonce = cryptonote::find_tx_extra_field_by_type(tx_extra_fields, nonce);
715  CHECK_AND_ASSERT_THROW_MES(has_nonce || !nonce_required, "Transaction nonce not present");
716 
717  if (nonce_required){
718  const std::string & payment_id = tdata.tsx_data.payment_id();
719  if (payment_id.size() == 32){
720  crypto::hash payment_id_long{};
721  CHECK_AND_ASSERT_THROW_MES(cryptonote::get_payment_id_from_tx_extra_nonce(nonce.nonce, payment_id_long), "Long payment ID not present");
722 
723  } else if (payment_id.size() == 8){
724  crypto::hash8 payment_id_short{};
725  CHECK_AND_ASSERT_THROW_MES(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(nonce.nonce, payment_id_short), "Short payment ID not present");
726  }
727  }
728  }
729 
730 #else //WITH_DEVICE_TREZOR
731 
732  void register_all(std::map<std::string, std::unique_ptr<device>> &registry) {
733  }
734 
735  void register_all() {
736  }
737 
738 #endif //WITH_DEVICE_TREZOR
739 }}
const char * res
Definition: hmac_keccak.cpp:41
boost::optional< unsigned > client_version
Definition: device_cold.hpp:49
void generate_commitment(std::vector< ElectroneumTransferDetails > &mtds, const std::vector< tools::wallet2::transfer_details > &transfers, std::shared_ptr< messages::Electroneum::ElectroneumKeyImageExportInitRequest > &req)
Definition: protocol.cpp:188
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
std::vector< crypto::key_image > key_images
Definition: wallet2.h:506
#define MERROR(x)
Definition: misc_log_ex.h:73
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
Definition: misc_log_ex.h:173
void register_all(std::map< std::string, std::unique_ptr< device >> &registry)
#define MTRACE(x)
Definition: misc_log_ex.h:77
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)
Definition: protocol.cpp:1051
cryptonote::transaction tx
Definition: protocol.hpp:178
#define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val)
::std::string string
Definition: gtest-port.h:1097
epee::misc_utils::struct_init< response_t > response
POD_CLASS key_derivation
Definition: crypto.h:98
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
Definition: misc_log_ex.h:181
std::vector< std::string > tx_device_aux
Definition: device_cold.hpp:46
boost::variant< txin_gen, txin_to_script, txin_to_scripthash, txin_to_key, txin_to_key_public > txin_v
std::vector< pending_tx > ptx
Definition: wallet2.h:505
epee::mlocked< tools::scrubbed< ec_scalar > > secret_key
Definition: crypto.h:82
bool register_device(const std::string &device_name, device *hw_device)
Definition: device.cpp:100
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
unsigned char uint8_t
Definition: stdint.h:124
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
Definition: protocol.cpp:1038
#define HW_TREZOR_NAME
std::vector< uint8_t > extra
tools::wallet2::unsigned_tx_set unsigned_tx_set
Definition: protocol.hpp:157
std::vector< cryptonote::tx_source_entry > sources
Definition: wallet2.h:422
#define MDEBUG(x)
Definition: misc_log_ex.h:76
std::vector< tx_construction_data > txes
Definition: wallet2.h:499
const crypto::secret_key null_skey
Definition: crypto.cpp:73
void load_tx_key_data(hw::device_cold::tx_key_data_t &res, const std::string &data)
Definition: protocol.cpp:1007
std::pair< size_t, wallet2::transfer_container > transfers
Definition: wallet2.h:500
unsigned __int64 uint64_t
Definition: stdint.h:136
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)
Definition: device.cpp:38
bool parse_and_validate_tx_from_blob(const blobdata &tx_blob, transaction &tx)
cryptonote::account_public_address get_address(const var_addr_t &inp)
Definition: chaingen.cpp:665
POD_CLASS public_key
Definition: crypto.h:76
#define MWARNING(x)
Definition: misc_log_ex.h:74
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)
Definition: transport.cpp:90
std::string blobdata
Definition: blobdatatype.h:39
CXA_THROW_INFO_T * info
Definition: stack_trace.cpp:91
void decrypt(const void *ciphertext, size_t length, const uint8_t *key, const uint8_t *iv, char *plaintext, size_t *plaintext_len)
Definition: protocol.cpp:120
POD_CLASS signature
Definition: crypto.h:108
POD_CLASS hash8
Definition: hash.h:53
POD_CLASS key_image
Definition: crypto.h:102
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)
Definition: protocol.cpp:224
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
Definition: device_cold.hpp:48
POD_CLASS hash
Definition: hash.h:50
bool key_image_data(wallet_shim *wallet, const std::vector< tools::wallet2::transfer_details > &transfers, std::vector< ElectroneumTransferDetails > &res)
Definition: protocol.cpp:145
const char * spendkey
Definition: multisig.cpp:38
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
Definition: device_cold.hpp:55