Electroneum
Loading...
Searching...
No Matches
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
32namespace hw {
33namespace 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
249 void device_trezor::load_tx_key_data(::hw::device_cold::tx_key_data_t & res, const std::string & tx_aux_data)
250 {
252 }
253
254 void device_trezor::get_tx_key(
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){
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,
454 const ::crypto::public_key& out_key,
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,
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,
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 {
519 CHECKED_GET_SPECIFIC_VARIANT(s_e, const cryptonote::txin_to_key, in, false);
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
736 }
737
738#endif //WITH_DEVICE_TREZOR
739}}
cryptonote::account_public_address get_address(const var_addr_t &inp)
Definition chaingen.cpp:665
std::vector< std::pair< crypto::key_image, crypto::signature > > exported_key_image
cryptonote::transaction tx
Definition protocol.hpp:178
std::vector< std::string > tx_device_aux
boost::optional< unsigned > client_version
boost::optional< int > bp_version
#define TREZOR_AUTO_LOCK_DEVICE()
#define TREZOR_AUTO_LOCK_CMD()
void * memcpy(void *a, const void *b, size_t c)
const char * res
#define MERROR(x)
Definition misc_log_ex.h:73
#define MWARNING(x)
Definition misc_log_ex.h:74
#define MDEBUG(x)
Definition misc_log_ex.h:76
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
#define MTRACE(x)
Definition misc_log_ex.h:77
POD_CLASS signature
Definition crypto.h:108
const crypto::secret_key null_skey
Definition crypto.cpp:73
epee::mlocked< tools::scrubbed< ec_scalar > > secret_key
Definition crypto.h:82
POD_CLASS hash8
Definition hash.h:53
POD_CLASS key_derivation
Definition crypto.h:101
POD_CLASS public_key
Definition crypto.h:79
POD_CLASS key_image
Definition crypto.h:105
POD_CLASS hash
Definition hash.h:50
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
boost::variant< txin_gen, txin_to_script, txin_to_scripthash, txin_to_key, txin_to_key_public > txin_v
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
std::string blobdata
bool parse_and_validate_tx_from_blob(const blobdata &tx_blob, transaction &tx)
bool t_serializable_object_to_blob(const t_object &to, blobdata &b_blob)
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
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 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 key_image_data(wallet_shim *wallet, const std::vector< tools::wallet2::transfer_details > &transfers, std::vector< ElectroneumTransferDetails > &res)
Definition protocol.cpp:145
void load_tx_key_data(hw::device_cold::tx_key_data_t &res, const std::string &data)
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > 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::Electroneum::ElectroneumGetTxKeyAck > ack)
void register_all()
uint64_t pack_version(uint32_t major, uint32_t minor, uint32_t patch)
Definition transport.cpp:90
Definition device.cpp:38
bool register_device(const std::string &device_name, device *hw_device)
Definition device.cpp:100
CXA_THROW_INFO_T * info
unsigned char uint8_t
Definition stdint.h:124
unsigned __int64 uint64_t
Definition stdint.h:136
std::vector< crypto::key_image > key_images
Definition wallet2.h:506
std::vector< pending_tx > ptx
Definition wallet2.h:505
std::vector< cryptonote::tx_source_entry > sources
Definition wallet2.h:422
std::vector< cryptonote::tx_destination_entry > splitted_dsts
Definition wallet2.h:424
std::vector< tx_construction_data > txes
Definition wallet2.h:499
std::pair< size_t, wallet2::transfer_container > transfers
Definition wallet2.h:500
const char * spendkey
Definition multisig.cpp:38
#define HW_TREZOR_NAME