Electroneum
protocol.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 "protocol.hpp"
31 #include <unordered_map>
32 #include <set>
33 #include <utility>
34 #include <boost/endian/conversion.hpp>
36 #include <common/json_util.h>
37 #include <crypto/hmac-keccak.h>
38 #include <ringct/rctSigs.h>
39 #include <ringct/bulletproofs.h>
40 #include "cryptonote_config.h"
41 #include <sodium.h>
43 #include <sodium/crypto_aead_chacha20poly1305.h>
44 
45 #define GET_FIELD_STRING(name, type, jtype) field_##name = std::string(json[#name].GetString(), json[#name].GetStringLength())
46 #define GET_FIELD_OTHER(name, type, jtype) field_##name = static_cast<type>(json[#name].Get##jtype())
47 
48 #define GET_STRING_FROM_JSON(json, name, type, mandatory, def) \
49  GET_FIELD_FROM_JSON_EX(json, name, type, String, mandatory, def, GET_FIELD_STRING)
50 
51 #define GET_FIELD_FROM_JSON(json, name, type, jtype, mandatory, def) \
52  GET_FIELD_FROM_JSON_EX(json, name, type, jtype, mandatory, def, GET_FIELD_OTHER)
53 
54 #define GET_FIELD_FROM_JSON_EX(json, name, type, jtype, mandatory, def, VAL) \
55  type field_##name = static_cast<type>(def); \
56  bool field_##name##_found = false; \
57  (void)field_##name##_found; \
58  do if (json.HasMember(#name)) \
59  { \
60  if (json[#name].Is##jtype()) \
61  { \
62  VAL(name, type, jtype); \
63  field_##name##_found = true; \
64  } \
65  else \
66  { \
67  throw std::invalid_argument("Field " #name " found in JSON, but not " #jtype); \
68  } \
69  } \
70  else if (mandatory) \
71  { \
72  throw std::invalid_argument("Field " #name " not found in JSON");\
73  } while(0)
74 
75 
76 namespace hw{
77 namespace trezor{
78 namespace protocol{
79 
81  return std::string(key.data, sizeof(key.data));
82  }
83 
85  return std::string(key.data, sizeof(key.data));
86  }
87 
89  return std::string(key.data, sizeof(key.data));
90  }
91 
93  return std::string(reinterpret_cast<const char*>(key.bytes), sizeof(key.bytes));
94  }
95 
97  if (str.size() != sizeof(key.data)){
98  throw std::invalid_argument(std::string("Key has to have ") + std::to_string(sizeof(key.data)) + " B");
99  }
100  memcpy(key.data, str.data(), sizeof(key.data));
101  }
102 
104  if (str.size() != sizeof(key.data)){
105  throw std::invalid_argument(std::string("Key has to have ") + std::to_string(sizeof(key.data)) + " B");
106  }
107  memcpy(key.data, str.data(), sizeof(key.data));
108  }
109 
110  void string_to_key(::rct::key & key, const std::string & str){
111  if (str.size() != sizeof(key.bytes)){
112  throw std::invalid_argument(std::string("Key has to have ") + std::to_string(sizeof(key.bytes)) + " B");
113  }
114  memcpy(key.bytes, str.data(), sizeof(key.bytes));
115  }
116 
117 namespace crypto {
118 namespace chacha {
119 
120  void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext, size_t *plaintext_len){
121  CHECK_AND_ASSERT_THROW_MES(length >= TAG_SIZE, "Ciphertext length too small");
122  CHECK_AND_ASSERT_THROW_MES(!plaintext_len || *plaintext_len >= (length - TAG_SIZE), "Plaintext length too small");
123 
124  unsigned long long int res_len = plaintext_len ? *plaintext_len : length;
125  auto r = crypto_aead_chacha20poly1305_ietf_decrypt(
126  reinterpret_cast<unsigned char *>(plaintext), &res_len, nullptr,
127  static_cast<const unsigned char *>(ciphertext), length, nullptr, 0, iv, key);
128 
129  if (r != 0){
130  throw exc::Poly1305TagInvalid();
131  }
132 
133  if (plaintext_len){
134  *plaintext_len = (size_t) res_len;
135  }
136  }
137 
138 }
139 }
140 
141 
142 // Cold Key image sync
143 namespace ki {
144 
146  const std::vector<tools::wallet2::transfer_details> & transfers,
147  std::vector<ElectroneumTransferDetails> & res)
148  {
149  for(auto & td : transfers){
151  const std::vector<::crypto::public_key> additional_tx_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(td.m_tx);
152 
153  res.emplace_back();
154  auto & cres = res.back();
155 
156  cres.set_out_key(key_to_string(boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key));
157  cres.set_tx_pub_key(key_to_string(tx_pub_key));
158  cres.set_internal_output_index(td.m_internal_output_index);
159  for(auto & aux : additional_tx_pub_keys){
160  cres.add_additional_tx_pub_keys(key_to_string(aux));
161  }
162  }
163 
164  return true;
165  }
166 
168  KECCAK_CTX kck;
169  uint8_t md[32];
170 
171  CHECK_AND_ASSERT_THROW_MES(rr.out_key().size() == 32, "Invalid out_key size");
172  CHECK_AND_ASSERT_THROW_MES(rr.tx_pub_key().size() == 32, "Invalid tx_pub_key size");
173 
174  keccak_init(&kck);
175  keccak_update(&kck, reinterpret_cast<const uint8_t *>(rr.out_key().data()), 32);
176  keccak_update(&kck, reinterpret_cast<const uint8_t *>(rr.tx_pub_key().data()), 32);
177  for (const auto &aux : rr.additional_tx_pub_keys()){
178  CHECK_AND_ASSERT_THROW_MES(aux.size() == 32, "Invalid aux size");
179  keccak_update(&kck, reinterpret_cast<const uint8_t *>(aux.data()), 32);
180  }
181 
182  auto index_serialized = tools::get_varint_data(rr.internal_output_index());
183  keccak_update(&kck, reinterpret_cast<const uint8_t *>(index_serialized.data()), index_serialized.size());
184  keccak_finish(&kck, md);
185  return std::string(reinterpret_cast<const char*>(md), sizeof(md));
186  }
187 
188  void generate_commitment(std::vector<ElectroneumTransferDetails> & mtds,
189  const std::vector<tools::wallet2::transfer_details> & transfers,
190  std::shared_ptr<messages::Electroneum::ElectroneumKeyImageExportInitRequest> & req)
191  {
192  req = std::make_shared<messages::Electroneum::ElectroneumKeyImageExportInitRequest>();
193 
194  KECCAK_CTX kck;
195  uint8_t final_hash[32];
196  keccak_init(&kck);
197 
198  for(auto &cur : mtds){
199  auto hash = compute_hash(cur);
200  keccak_update(&kck, reinterpret_cast<const uint8_t *>(hash.data()), hash.size());
201  }
202  keccak_finish(&kck, final_hash);
203 
204  req = std::make_shared<messages::Electroneum::ElectroneumKeyImageExportInitRequest>();
205  req->set_hash(std::string(reinterpret_cast<const char*>(final_hash), 32));
206  req->set_num(transfers.size());
207 
208  std::unordered_map<uint32_t, std::set<uint32_t>> sub_indices;
209  for (auto &cur : transfers){
210  auto search = sub_indices.emplace(cur.m_subaddr_index.major, std::set<uint32_t>());
211  auto & st = search.first->second;
212  st.insert(cur.m_subaddr_index.minor);
213  }
214 
215  for (auto& x: sub_indices){
216  auto subs = req->add_subs();
217  subs->set_account(x.first);
218  for(auto minor : x.second){
219  subs->add_minor_indices(minor);
220  }
221  }
222  }
223 
226  const std::shared_ptr<messages::Electroneum::ElectroneumLiveRefreshStepAck> & ack,
227  ::cryptonote::keypair& in_ephemeral,
228  ::crypto::key_image& ki)
229  {
230  std::string str_out_key(out_key.data, sizeof(out_key.data));
231  auto enc_key = protocol::tx::compute_enc_key(view_key_priv, str_out_key, ack->salt());
232 
233  const size_t len_ciphertext = ack->key_image().size(); // IV || keys
235 
236  size_t ki_len = len_ciphertext - crypto::chacha::IV_SIZE - crypto::chacha::TAG_SIZE;
237  std::unique_ptr<uint8_t[]> plaintext(new uint8_t[ki_len]);
238  uint8_t * buff = plaintext.get();
239 
241  ack->key_image().data() + crypto::chacha::IV_SIZE,
242  len_ciphertext - crypto::chacha::IV_SIZE,
243  reinterpret_cast<const uint8_t *>(enc_key.data),
244  reinterpret_cast<const uint8_t *>(ack->key_image().data()),
245  reinterpret_cast<char *>(buff), &ki_len);
246 
247  CHECK_AND_ASSERT_THROW_MES(ki_len == 3*32, "Invalid size");
248  ::crypto::signature sig{};
249  memcpy(ki.data, buff, 32);
250  memcpy(sig.c.data, buff + 32, 32);
251  memcpy(sig.r.data, buff + 64, 32);
252  in_ephemeral.pub = out_key;
253  in_ephemeral.sec = ::crypto::null_skey;
254 
255  // Verification
256  std::vector<const ::crypto::public_key*> pkeys;
257  pkeys.push_back(&out_key);
258 
260  "Key image out of validity domain: key image " << epee::string_tools::pod_to_hex(ki));
261 
263  "Signature failed for key image " << epee::string_tools::pod_to_hex(ki)
264  << ", signature " + epee::string_tools::pod_to_hex(sig)
265  << ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0]));
266  }
267 }
268 
269 // Cold transaction signing
270 namespace tx {
271 
273  dst->set_view_public_key(key_to_string(src->m_view_public_key));
274  dst->set_spend_public_key(key_to_string(src->m_spend_public_key));
275  }
276 
278  dst->set_amount(src->amount);
279  dst->set_is_subaddress(src->is_subaddress);
280  dst->set_is_integrated(src->is_integrated);
281  dst->set_original(src->original);
282  translate_address(dst->mutable_addr(), &(src->addr));
283  }
284 
286  for(auto & cur : src->outputs){
287  auto out = dst->add_outputs();
288  out->set_idx(cur.first);
289  translate_rct_key(out->mutable_key(), &(cur.second));
290  }
291 
292  dst->set_real_output(src->real_output);
293  dst->set_real_out_tx_key(key_to_string(src->real_out_tx_key));
294  for(auto & cur : src->real_out_additional_tx_keys){
295  dst->add_real_out_additional_tx_keys(key_to_string(cur));
296  }
297 
298  dst->set_real_output_in_tx_index(src->real_output_in_tx_index);
299  dst->set_amount(src->amount);
300  dst->set_rct(src->rct);
301  dst->set_mask(key_to_string(src->mask));
302  translate_klrki(dst->mutable_multisig_klrki(), &(src->multisig_kLRki));
303  }
304 
306  dst->set_k(key_to_string(src->k));
307  dst->set_l(key_to_string(src->L));
308  dst->set_r(key_to_string(src->R));
309  dst->set_ki(key_to_string(src->ki));
310  }
311 
313  dst->set_dest(key_to_string(src->dest));
314  dst->set_commitment(key_to_string(src->mask));
315  }
316 
317  std::string hash_addr(const ElectroneumAccountPublicAddress * addr, boost::optional<uint64_t> amount, boost::optional<bool> is_subaddr){
318  return hash_addr(addr->spend_public_key(), addr->view_public_key(), amount, is_subaddr);
319  }
320 
321  std::string hash_addr(const std::string & spend_key, const std::string & view_key, boost::optional<uint64_t> amount, boost::optional<bool> is_subaddr){
322  ::crypto::public_key spend{}, view{};
323  if (spend_key.size() != 32 || view_key.size() != 32){
324  throw std::invalid_argument("Public keys have invalid sizes");
325  }
326 
327  memcpy(spend.data, spend_key.data(), 32);
328  memcpy(view.data, view_key.data(), 32);
329  return hash_addr(&spend, &view, amount, is_subaddr);
330  }
331 
332  std::string hash_addr(const ::crypto::public_key * spend_key, const ::crypto::public_key * view_key, boost::optional<uint64_t> amount, boost::optional<bool> is_subaddr){
333  char buff[64+8+1];
334  size_t offset = 0;
335 
336  memcpy(buff + offset, spend_key->data, 32); offset += 32;
337  memcpy(buff + offset, view_key->data, 32); offset += 32;
338 
339  if (amount){
340  memcpy(buff + offset, (uint8_t*) &(amount.get()), sizeof(amount.get())); offset += sizeof(amount.get());
341  }
342 
343  if (is_subaddr){
344  buff[offset] = is_subaddr.get();
345  offset += 1;
346  }
347 
348  return std::string(buff, offset);
349  }
350 
352  {
353  uint8_t hash[32];
354  KECCAK_CTX ctx;
356 
357  keccak_init(&ctx);
358  keccak_update(&ctx, (const uint8_t *) private_view_key.data, sizeof(private_view_key.data));
359  if (!aux.empty()){
360  keccak_update(&ctx, (const uint8_t *) aux.data(), aux.size());
361  }
362  keccak_finish(&ctx, hash);
363  keccak(hash, sizeof(hash), hash, sizeof(hash));
364 
365  hmac_keccak_hash(hash, (const uint8_t *) salt.data(), salt.size(), hash, sizeof(hash));
366  memcpy(res.data, hash, sizeof(hash));
367  memwipe(hash, sizeof(hash));
368  return res;
369  }
370 
372  rsig_type = 0;
373  bp_version = 0;
374  cur_input_idx = 0;
375  cur_output_idx = 0;
376  cur_batch_idx = 0;
378  }
379 
380  Signer::Signer(wallet_shim *wallet2, const unsigned_tx_set * unsigned_tx, size_t tx_idx, hw::tx_aux_data * aux_data) {
381  m_wallet2 = wallet2;
382  m_unsigned_tx = unsigned_tx;
383  m_aux_data = aux_data;
384  m_tx_idx = tx_idx;
385  m_ct.tx_data = cur_tx();
386  m_multisig = false;
387  m_client_version = 1;
388  }
389 
390  void Signer::extract_payment_id(){
391  const std::vector<uint8_t>& tx_extra = cur_tx().extra;
392  m_ct.tsx_data.set_payment_id("");
393 
394  std::vector<cryptonote::tx_extra_field> tx_extra_fields;
395  cryptonote::parse_tx_extra(tx_extra, tx_extra_fields); // ok if partially parsed
396  cryptonote::tx_extra_nonce extra_nonce;
397 
398  ::crypto::hash payment_id{};
399  if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
400  {
401  ::crypto::hash8 payment_id8{};
403  {
404  m_ct.tsx_data.set_payment_id(std::string(payment_id8.data, 8));
405  }
406  else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
407  {
408  m_ct.tsx_data.set_payment_id(std::string(payment_id.data, 32));
409  }
410  }
411  }
412 
413  static unsigned get_rsig_type(const rct::RCTConfig &rct_config, size_t num_outputs){
414  if (rct_config.range_proof_type == rct::RangeProofBorromean){
416  } else if (num_outputs > BULLETPROOF_MAX_OUTPUTS){
418  } else {
420  }
421  }
422 
423  static void generate_rsig_batch_sizes(std::vector<uint64_t> &batches, unsigned rsig_type, size_t num_outputs){
424  size_t amount_batched = 0;
425 
426  while(amount_batched < num_outputs){
427  if (rsig_type == rct::RangeProofBorromean || rsig_type == rct::RangeProofBulletproof) {
428  batches.push_back(1);
429  amount_batched += 1;
430 
431  } else if (rsig_type == rct::RangeProofPaddedBulletproof){
432  if (num_outputs > BULLETPROOF_MAX_OUTPUTS){
433  throw std::invalid_argument("BP padded can support only BULLETPROOF_MAX_OUTPUTS statements");
434  }
435  batches.push_back(num_outputs);
436  amount_batched += num_outputs;
437 
438  } else if (rsig_type == rct::RangeProofMultiOutputBulletproof){
439  size_t batch_size = 1;
440  while (batch_size * 2 + amount_batched <= num_outputs && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS){
441  batch_size *= 2;
442  }
443  batch_size = std::min(batch_size, num_outputs - amount_batched);
444  batches.push_back(batch_size);
445  amount_batched += batch_size;
446 
447  } else {
448  throw std::invalid_argument("Unknown rsig type");
449  }
450  }
451  }
452 
453  void Signer::compute_integrated_indices(TsxData * tsx_data){
454  if (m_aux_data == nullptr || m_aux_data->tx_recipients.empty()){
455  return;
456  }
457 
458  auto & chg = tsx_data->change_dts();
459  std::string change_hash = hash_addr(&chg.addr(), chg.amount(), chg.is_subaddress());
460 
461  std::vector<uint32_t> integrated_indices;
462  std::set<std::string> integrated_hashes;
463  for (auto & cur : m_aux_data->tx_recipients){
464  if (!cur.has_payment_id){
465  continue;
466  }
467  integrated_hashes.emplace(hash_addr(&cur.address.m_spend_public_key, &cur.address.m_view_public_key));
468  }
469 
470  ssize_t idx = -1;
471  for (auto & cur : tsx_data->outputs()){
472  idx += 1;
473 
474  std::string c_hash = hash_addr(&cur.addr(), cur.amount(), cur.is_subaddress());
475  if (c_hash == change_hash || cur.is_subaddress()){
476  continue;
477  }
478 
479  c_hash = hash_addr(&cur.addr());
480  if (integrated_hashes.find(c_hash) != integrated_hashes.end()){
481  integrated_indices.push_back((uint32_t)idx);
482  }
483  }
484 
485  if (!integrated_indices.empty()){
486  assign_to_repeatable(tsx_data->mutable_integrated_indices(), integrated_indices.begin(), integrated_indices.end());
487  }
488  }
489 
490  std::shared_ptr<messages::Electroneum::ElectroneumTransactionInitRequest> Signer::step_init(){
491  // extract payment ID from construction data
492  auto & tsx_data = m_ct.tsx_data;
493  auto & tx = cur_tx();
494 
495  m_ct.tx.version = 4;
496  m_ct.tx.unlock_time = tx.unlock_time;
497  m_client_version = (m_aux_data->client_version ? m_aux_data->client_version.get() : 1);
498 
499  tsx_data.set_version(1);
500  tsx_data.set_client_version(client_version());
501  tsx_data.set_unlock_time(tx.unlock_time);
502  tsx_data.set_num_inputs(static_cast<google::protobuf::uint32>(tx.sources.size()));
503  tsx_data.set_mixin(static_cast<google::protobuf::uint32>(tx.sources[0].outputs.size() - 1));
504  tsx_data.set_account(tx.subaddr_account);
505  assign_to_repeatable(tsx_data.mutable_minor_indices(), tx.subaddr_indices.begin(), tx.subaddr_indices.end());
506 
507  // Rsig decision
508  auto rsig_data = tsx_data.mutable_rsig_data();
509  m_ct.rsig_type = get_rsig_type(tx.rct_config, tx.splitted_dsts.size());
510  rsig_data->set_rsig_type(m_ct.rsig_type);
511  if (tx.rct_config.range_proof_type != rct::RangeProofBorromean){
512  m_ct.bp_version = (m_aux_data->bp_version ? m_aux_data->bp_version.get() : 1);
513  rsig_data->set_bp_version((uint32_t) m_ct.bp_version);
514  }
515 
516  generate_rsig_batch_sizes(m_ct.grouping_vct, m_ct.rsig_type, tx.splitted_dsts.size());
517  assign_to_repeatable(rsig_data->mutable_grouping(), m_ct.grouping_vct.begin(), m_ct.grouping_vct.end());
518 
519  translate_dst_entry(tsx_data.mutable_change_dts(), &(tx.change_dts));
520  for(auto & cur : tx.splitted_dsts){
521  auto dst = tsx_data.mutable_outputs()->Add();
522  translate_dst_entry(dst, &cur);
523  }
524 
525  compute_integrated_indices(&tsx_data);
526 
527  int64_t fee = 0;
528  for(auto & cur_in : tx.sources){
529  fee += cur_in.amount;
530  }
531  for(auto & cur_out : tx.splitted_dsts){
532  fee -= cur_out.amount;
533  }
534  if (fee < 0){
535  throw std::invalid_argument("Fee cannot be negative");
536  }
537 
538  tsx_data.set_fee(static_cast<google::protobuf::uint64>(fee));
539  this->extract_payment_id();
540 
541  auto init_req = std::make_shared<messages::Electroneum::ElectroneumTransactionInitRequest>();
542  init_req->set_version(0);
543  init_req->mutable_tsx_data()->CopyFrom(tsx_data);
544  return init_req;
545  }
546 
547  void Signer::step_init_ack(std::shared_ptr<const messages::Electroneum::ElectroneumTransactionInitAck> ack){
548  if (ack->has_rsig_data()){
549  m_ct.rsig_param = std::make_shared<ElectroneumRsigData>(ack->rsig_data());
550  }
551 
552  assign_from_repeatable(&(m_ct.tx_out_entr_hmacs), ack->hmacs().begin(), ack->hmacs().end());
553  }
554 
555  std::shared_ptr<messages::Electroneum::ElectroneumTransactionSetInputRequest> Signer::step_set_input(size_t idx){
556  CHECK_AND_ASSERT_THROW_MES(idx < cur_tx().sources.size(), "Invalid source index");
557  m_ct.cur_input_idx = idx;
558  auto res = std::make_shared<messages::Electroneum::ElectroneumTransactionSetInputRequest>();
559  translate_src_entry(res->mutable_src_entr(), &(cur_tx().sources[idx]));
560  return res;
561  }
562 
563  void Signer::step_set_input_ack(std::shared_ptr<const messages::Electroneum::ElectroneumTransactionSetInputAck> ack){
564  auto & vini_str = ack->vini();
565 
566  cryptonote::txin_v vini;
567  if (!cn_deserialize(vini_str.data(), vini_str.size(), vini)){
568  throw exc::ProtocolException("Cannot deserialize vin[i]");
569  }
570 
571  m_ct.tx.vin.emplace_back(vini);
572  m_ct.tx_in_hmacs.push_back(ack->vini_hmac());
573  m_ct.pseudo_outs.push_back(ack->pseudo_out());
574  m_ct.pseudo_outs_hmac.push_back(ack->pseudo_out_hmac());
575  m_ct.alphas.push_back(ack->pseudo_out_alpha());
576  m_ct.spend_encs.push_back(ack->spend_key());
577  }
578 
580  const size_t input_size = cur_tx().sources.size();
581 
582  m_ct.source_permutation.clear();
583  for (size_t n = 0; n < input_size; ++n){
584  m_ct.source_permutation.push_back(n);
585  }
586 
587  CHECK_AND_ASSERT_THROW_MES(m_ct.tx.vin.size() == input_size, "Invalid vector size");
588  std::sort(m_ct.source_permutation.begin(), m_ct.source_permutation.end(), [&](const size_t i0, const size_t i1) {
589  const cryptonote::txin_to_key &tk0 = boost::get<cryptonote::txin_to_key>(m_ct.tx.vin[i0]);
590  const cryptonote::txin_to_key &tk1 = boost::get<cryptonote::txin_to_key>(m_ct.tx.vin[i1]);
591  return memcmp(&tk0.k_image, &tk1.k_image, sizeof(tk0.k_image)) > 0;
592  });
593 
594  CHECK_AND_ASSERT_THROW_MES(m_ct.tx_in_hmacs.size() == input_size, "Invalid vector size");
595  CHECK_AND_ASSERT_THROW_MES(m_ct.pseudo_outs.size() == input_size, "Invalid vector size");
596  CHECK_AND_ASSERT_THROW_MES(m_ct.pseudo_outs_hmac.size() == input_size, "Invalid vector size");
597  CHECK_AND_ASSERT_THROW_MES(m_ct.alphas.size() == input_size, "Invalid vector size");
598  CHECK_AND_ASSERT_THROW_MES(m_ct.spend_encs.size() == input_size, "Invalid vector size");
599  CHECK_AND_ASSERT_THROW_MES(m_ct.tx_data.sources.size() == input_size, "Invalid vector size");
600 
601  tools::apply_permutation(m_ct.source_permutation, [&](size_t i0, size_t i1){
602  std::swap(m_ct.tx.vin[i0], m_ct.tx.vin[i1]);
603  std::swap(m_ct.tx_in_hmacs[i0], m_ct.tx_in_hmacs[i1]);
604  std::swap(m_ct.pseudo_outs[i0], m_ct.pseudo_outs[i1]);
605  std::swap(m_ct.pseudo_outs_hmac[i0], m_ct.pseudo_outs_hmac[i1]);
606  std::swap(m_ct.alphas[i0], m_ct.alphas[i1]);
607  std::swap(m_ct.spend_encs[i0], m_ct.spend_encs[i1]);
608  std::swap(m_ct.tx_data.sources[i0], m_ct.tx_data.sources[i1]);
609  });
610  }
611 
612  std::shared_ptr<messages::Electroneum::ElectroneumTransactionInputsPermutationRequest> Signer::step_permutation(){
613  sort_ki();
614 
615  auto res = std::make_shared<messages::Electroneum::ElectroneumTransactionInputsPermutationRequest>();
616  assign_to_repeatable(res->mutable_perm(), m_ct.source_permutation.begin(), m_ct.source_permutation.end());
617 
618  return res;
619  }
620 
621  void Signer::step_permutation_ack(std::shared_ptr<const messages::Electroneum::ElectroneumTransactionInputsPermutationAck> ack){
622 
623  }
624 
625  std::shared_ptr<messages::Electroneum::ElectroneumTransactionInputViniRequest> Signer::step_set_vini_input(size_t idx){
626  CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.sources.size(), "Invalid transaction index");
627  CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx.vin.size(), "Invalid transaction index");
628  CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_in_hmacs.size(), "Invalid transaction index");
629 
630  m_ct.cur_input_idx = idx;
631  auto tx = m_ct.tx_data;
632  auto res = std::make_shared<messages::Electroneum::ElectroneumTransactionInputViniRequest>();
633  auto & vini = m_ct.tx.vin[idx];
634  translate_src_entry(res->mutable_src_entr(), &(tx.sources[idx]));
636  res->set_vini_hmac(m_ct.tx_in_hmacs[idx]);
637 
638  if (client_version() == 0) {
639  CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index");
640  CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index");
641  res->set_pseudo_out(m_ct.pseudo_outs[idx]);
642  res->set_pseudo_out_hmac(m_ct.pseudo_outs_hmac[idx]);
643  }
644 
645  return res;
646  }
647 
648  void Signer::step_set_vini_input_ack(std::shared_ptr<const messages::Electroneum::ElectroneumTransactionInputViniAck> ack){
649 
650  }
651 
652  std::shared_ptr<messages::Electroneum::ElectroneumTransactionAllInputsSetRequest> Signer::step_all_inputs_set(){
653  return std::make_shared<messages::Electroneum::ElectroneumTransactionAllInputsSetRequest>();
654  }
655 
656  void Signer::step_all_inputs_set_ack(std::shared_ptr<const messages::Electroneum::ElectroneumTransactionAllInputsSetAck> ack){
657  if (client_version() > 0 || !is_offloading()){
658  return;
659  }
660 
661  // If offloading, expect rsig configuration.
662  if (!ack->has_rsig_data()){
663  throw exc::ProtocolException("Rsig offloading requires rsig param");
664  }
665 
666  auto & rsig_data = ack->rsig_data();
667  if (!rsig_data.has_mask()){
668  throw exc::ProtocolException("Gamma masks not present in offloaded version");
669  }
670 
671  auto & mask = rsig_data.mask();
672  if (mask.size() != 32 * num_outputs()){
673  throw exc::ProtocolException("Invalid number of gamma masks");
674  }
675 
676  m_ct.rsig_gamma.reserve(num_outputs());
677  for(size_t c=0; c < num_outputs(); ++c){
678  rct::key cmask{};
679  memcpy(cmask.bytes, mask.data() + c * 32, 32);
680  m_ct.rsig_gamma.emplace_back(cmask);
681  }
682  }
683 
684  std::shared_ptr<messages::Electroneum::ElectroneumTransactionSetOutputRequest> Signer::step_set_output(size_t idx){
685  CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.splitted_dsts.size(), "Invalid transaction index");
686  CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_out_entr_hmacs.size(), "Invalid transaction index");
687  CHECK_AND_ASSERT_THROW_MES(is_req_bulletproof(), "Borromean rsig not supported");
688 
689  m_ct.cur_output_idx = idx;
690  m_ct.cur_output_in_batch_idx += 1; // assumes sequential call to step_set_output()
691 
692  auto res = std::make_shared<messages::Electroneum::ElectroneumTransactionSetOutputRequest>();
693  auto & cur_dst = m_ct.tx_data.splitted_dsts[idx];
694  translate_dst_entry(res->mutable_dst_entr(), &cur_dst);
695  res->set_dst_entr_hmac(m_ct.tx_out_entr_hmacs[idx]);
696 
697  // Range sig offloading to the host
698  // ClientV0 sends offloaded BP with the last message in the batch.
699  // ClientV1 needs additional message after the last message in the batch as BP uses deterministic masks.
700  if (client_version() == 0 && is_offloading() && should_compute_bp_now()) {
701  auto rsig_data = res->mutable_rsig_data();
702  compute_bproof(*rsig_data);
703  }
704 
705  return res;
706  }
707 
708  void Signer::step_set_output_ack(std::shared_ptr<const messages::Electroneum::ElectroneumTransactionSetOutputAck> ack){
709  cryptonote::tx_out tx_out;
710  rct::Bulletproof bproof{};
711  rct::ctkey out_pk{};
712  rct::ecdhTuple ecdh{};
713 
714  bool has_rsig = false;
715  std::string rsig_buff;
716 
717  if (ack->has_rsig_data()){
718  auto & rsig_data = ack->rsig_data();
719 
720  if (rsig_data.has_rsig() && !rsig_data.rsig().empty()){
721  has_rsig = true;
722  rsig_buff = rsig_data.rsig();
723  }
724 
725  if (client_version() >= 1 && rsig_data.has_mask()){
726  rct::key cmask{};
727  string_to_key(cmask, rsig_data.mask());
728  m_ct.rsig_gamma.emplace_back(cmask);
729  }
730  }
731 
732  if (!cn_deserialize(ack->tx_out(), tx_out)){
733  throw exc::ProtocolException("Cannot deserialize vout[i]");
734  }
735 
736  if (!cn_deserialize(ack->out_pk(), out_pk)){
737  throw exc::ProtocolException("Cannot deserialize out_pk");
738  }
739 
740  if (m_ct.bp_version <= 1) {
741  if (!cn_deserialize(ack->ecdh_info(), ecdh)){
742  throw exc::ProtocolException("Cannot deserialize ecdhtuple");
743  }
744  } else {
745  CHECK_AND_ASSERT_THROW_MES(8 == ack->ecdh_info().size(), "Invalid ECDH.amount size");
746  memcpy(ecdh.amount.bytes, ack->ecdh_info().data(), 8);
747  }
748 
749  if (has_rsig && is_req_bulletproof() && !cn_deserialize(rsig_buff, bproof)){
750  throw exc::ProtocolException("Cannot deserialize bulletproof rangesig");
751  }
752 
753  m_ct.tx.vout.emplace_back(tx_out);
754  m_ct.tx_out_hmacs.push_back(ack->vouti_hmac());
755  m_ct.tx_out_pk.emplace_back(out_pk);
756  m_ct.tx_out_ecdh.emplace_back(ecdh);
757 
758  // ClientV0, if no rsig was generated on Trezor, do not continue.
759  // ClientV1+ generates BP after all masks in the current batch are generated
760  if (!has_rsig || (client_version() >= 1 && is_offloading())){
761  return;
762  }
763 
764  process_bproof(bproof);
765  m_ct.cur_batch_idx += 1;
766  m_ct.cur_output_in_batch_idx = 0;
767  }
768 
769  bool Signer::should_compute_bp_now() const {
770  CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index");
771  return m_ct.grouping_vct[m_ct.cur_batch_idx] <= m_ct.cur_output_in_batch_idx;
772  }
773 
774  void Signer::compute_bproof(messages::Electroneum::ElectroneumTransactionRsigData & rsig_data){
775  auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx];
776  std::vector<uint64_t> amounts;
777  rct::keyV masks;
778  CHECK_AND_ASSERT_THROW_MES(m_ct.cur_output_idx + 1 >= batch_size, "Invalid index for batching");
779 
780  for(size_t i = 0; i < batch_size; ++i){
781  const size_t bidx = 1 + m_ct.cur_output_idx - batch_size + i;
782  CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_data.splitted_dsts.size(), "Invalid gamma index");
783  CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.rsig_gamma.size(), "Invalid gamma index");
784 
785  amounts.push_back(m_ct.tx_data.splitted_dsts[bidx].amount);
786  masks.push_back(m_ct.rsig_gamma[bidx]);
787  }
788 
789  auto bp = bulletproof_PROVE(amounts, masks);
790  auto serRsig = cn_serialize(bp);
791  m_ct.tx_out_rsigs.emplace_back(bp);
792  rsig_data.set_rsig(serRsig);
793  }
794 
795  void Signer::process_bproof(rct::Bulletproof & bproof){
796  CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index");
797  auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx];
798  for (size_t i = 0; i < batch_size; ++i){
799  const size_t bidx = 1 + m_ct.cur_output_idx - batch_size + i;
800  CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_out_pk.size(), "Invalid out index");
801 
802  rct::key commitment = m_ct.tx_out_pk[bidx].mask;
803  commitment = rct::scalarmultKey(commitment, rct::INV_EIGHT);
804  bproof.V.push_back(commitment);
805  }
806 
807  m_ct.tx_out_rsigs.emplace_back(bproof);
808  if (!rct::bulletproof_VERIFY(boost::get<rct::Bulletproof>(m_ct.tx_out_rsigs.back()))) {
809  throw exc::ProtocolException("Returned range signature is invalid");
810  }
811  }
812 
813  std::shared_ptr<messages::Electroneum::ElectroneumTransactionSetOutputRequest> Signer::step_rsig(size_t idx){
814  if (client_version() == 0 || !is_offloading() || !should_compute_bp_now()){
815  return nullptr;
816  }
817 
818  auto res = std::make_shared<messages::Electroneum::ElectroneumTransactionSetOutputRequest>();
819  auto & cur_dst = m_ct.tx_data.splitted_dsts[idx];
820  translate_dst_entry(res->mutable_dst_entr(), &cur_dst);
821  res->set_dst_entr_hmac(m_ct.tx_out_entr_hmacs[idx]);
822 
823  compute_bproof(*(res->mutable_rsig_data()));
824  res->set_is_offloaded_bp(true);
825  return res;
826  }
827 
828  void Signer::step_set_rsig_ack(std::shared_ptr<const messages::Electroneum::ElectroneumTransactionSetOutputAck> ack){
829  m_ct.cur_batch_idx += 1;
830  m_ct.cur_output_in_batch_idx = 0;
831  }
832 
833  std::shared_ptr<messages::Electroneum::ElectroneumTransactionAllOutSetRequest> Signer::step_all_outs_set(){
834  return std::make_shared<messages::Electroneum::ElectroneumTransactionAllOutSetRequest>();
835  }
836 
837  void Signer::step_all_outs_set_ack(std::shared_ptr<const messages::Electroneum::ElectroneumTransactionAllOutSetAck> ack, hw::device &hwdev){
838  m_ct.rv = std::make_shared<rct::rctSig>();
839  m_ct.rv->txnFee = ack->rv().txn_fee();
840  m_ct.rv->type = static_cast<uint8_t>(ack->rv().rv_type());
841  string_to_key(m_ct.rv->message, ack->rv().message());
842 
843  // Extra copy
844  m_ct.tx.extra.clear();
845  auto extra = ack->extra();
846  auto extra_data = extra.data();
847  m_ct.tx.extra.reserve(extra.size());
848  for(size_t i = 0; i < extra.size(); ++i){
849  m_ct.tx.extra.push_back(static_cast<uint8_t>(extra_data[i]));
850  }
851 
852  ::crypto::hash tx_prefix_hash{};
853  cryptonote::get_transaction_prefix_hash(m_ct.tx, tx_prefix_hash);
854  m_ct.tx_prefix_hash = key_to_string(tx_prefix_hash);
855  if (crypto_verify_32(reinterpret_cast<const unsigned char *>(tx_prefix_hash.data),
856  reinterpret_cast<const unsigned char *>(ack->tx_prefix_hash().data()))){
857  throw exc::proto::SecurityException("Transaction prefix has does not match to the computed value");
858  }
859 
860  // RctSig
861  auto num_sources = m_ct.tx_data.sources.size();
862  if (is_simple() || is_req_bulletproof()){
863  auto dst = &m_ct.rv->pseudoOuts;
864  if (is_bulletproof()){
865  dst = &m_ct.rv->p.pseudoOuts;
866  }
867 
868  dst->clear();
869  for (const auto &pseudo_out : m_ct.pseudo_outs) {
870  dst->emplace_back();
871  string_to_key(dst->back(), pseudo_out);
872  }
873 
874  m_ct.rv->mixRing.resize(num_sources);
875  } else {
876  m_ct.rv->mixRing.resize(m_ct.tsx_data.mixin());
877  m_ct.rv->mixRing[0].resize(num_sources);
878  }
879 
880  CHECK_AND_ASSERT_THROW_MES(m_ct.tx_out_pk.size() == m_ct.tx_out_ecdh.size(), "Invalid vector sizes");
881  for(size_t i = 0; i < m_ct.tx_out_ecdh.size(); ++i){
882  m_ct.rv->outPk.push_back(m_ct.tx_out_pk[i]);
883  m_ct.rv->ecdhInfo.push_back(m_ct.tx_out_ecdh[i]);
884  }
885 
886  for(size_t i = 0; i < m_ct.tx_out_rsigs.size(); ++i){
887  if (is_bulletproof()){
888  m_ct.rv->p.bulletproofs.push_back(boost::get<rct::Bulletproof>(m_ct.tx_out_rsigs[i]));
889  } else {
890  m_ct.rv->p.rangeSigs.push_back(boost::get<rct::rangeSig>(m_ct.tx_out_rsigs[i]));
891  }
892  }
893 
894  rct::key hash_computed = rct::get_pre_mlsag_hash(*(m_ct.rv), hwdev);
895  auto & hash = ack->full_message_hash();
896 
897  if (hash.size() != 32){
898  throw exc::ProtocolException("Returned mlsag hash has invalid size");
899  }
900 
901  if (crypto_verify_32(reinterpret_cast<const unsigned char *>(hash_computed.bytes),
902  reinterpret_cast<const unsigned char *>(hash.data()))){
903  throw exc::proto::SecurityException("Computed MLSAG does not match");
904  }
905  }
906 
907  std::shared_ptr<messages::Electroneum::ElectroneumTransactionSignInputRequest> Signer::step_sign_input(size_t idx){
908  m_ct.cur_input_idx = idx;
909 
910  CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.sources.size(), "Invalid transaction index");
911  CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx.vin.size(), "Invalid transaction index");
912  CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_in_hmacs.size(), "Invalid transaction index");
913  CHECK_AND_ASSERT_THROW_MES(idx < m_ct.alphas.size(), "Invalid transaction index");
914  CHECK_AND_ASSERT_THROW_MES(idx < m_ct.spend_encs.size(), "Invalid transaction index");
915 
916  auto res = std::make_shared<messages::Electroneum::ElectroneumTransactionSignInputRequest>();
917  translate_src_entry(res->mutable_src_entr(), &(m_ct.tx_data.sources[idx]));
918  res->set_vini(cryptonote::t_serializable_object_to_blob(m_ct.tx.vin[idx]));
919  res->set_vini_hmac(m_ct.tx_in_hmacs[idx]);
920  res->set_pseudo_out_alpha(m_ct.alphas[idx]);
921  res->set_spend_key(m_ct.spend_encs[idx]);
922 
923  CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index");
924  CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index");
925  res->set_pseudo_out(m_ct.pseudo_outs[idx]);
926  res->set_pseudo_out_hmac(m_ct.pseudo_outs_hmac[idx]);
927  return res;
928  }
929 
930  void Signer::step_sign_input_ack(std::shared_ptr<const messages::Electroneum::ElectroneumTransactionSignInputAck> ack){
931  rct::mgSig mg;
932  if (!cn_deserialize(ack->signature(), mg)){
933  throw exc::ProtocolException("Cannot deserialize mg[i]");
934  }
935 
936  // Sync updated pseudo_outputs, client_version>=1, HF10+
937  if (client_version() >= 1 && ack->has_pseudo_out()){
938  CHECK_AND_ASSERT_THROW_MES(m_ct.cur_input_idx < m_ct.pseudo_outs.size(), "Invalid pseudo-out index");
939  m_ct.pseudo_outs[m_ct.cur_input_idx] = ack->pseudo_out();
940  if (is_bulletproof()){
941  CHECK_AND_ASSERT_THROW_MES(m_ct.cur_input_idx < m_ct.rv->p.pseudoOuts.size(), "Invalid pseudo-out index");
942  string_to_key(m_ct.rv->p.pseudoOuts[m_ct.cur_input_idx], ack->pseudo_out());
943  } else {
944  CHECK_AND_ASSERT_THROW_MES(m_ct.cur_input_idx < m_ct.rv->pseudoOuts.size(), "Invalid pseudo-out index");
945  string_to_key(m_ct.rv->pseudoOuts[m_ct.cur_input_idx], ack->pseudo_out());
946  }
947  }
948 
949  m_ct.rv->p.MGs.push_back(mg);
950  }
951 
952  std::shared_ptr<messages::Electroneum::ElectroneumTransactionFinalRequest> Signer::step_final(){
953  m_ct.tx.rct_signatures = *(m_ct.rv);
954  return std::make_shared<messages::Electroneum::ElectroneumTransactionFinalRequest>();
955  }
956 
957  void Signer::step_final_ack(std::shared_ptr<const messages::Electroneum::ElectroneumTransactionFinalAck> ack){
958  if (m_multisig){
959  auto & cout_key = ack->cout_key();
960  for(auto & cur : m_ct.couts){
961  if (cur.size() != crypto::chacha::IV_SIZE + 32){
962  throw std::invalid_argument("Encrypted cout has invalid length");
963  }
964 
965  char buff[32];
966  auto data = cur.data();
967 
968  crypto::chacha::decrypt(data + crypto::chacha::IV_SIZE, 32, reinterpret_cast<const uint8_t *>(cout_key.data()), reinterpret_cast<const uint8_t *>(data), buff);
969  m_ct.couts_dec.emplace_back(buff, 32);
970  }
971  }
972 
973  m_ct.enc_salt1 = ack->salt();
974  m_ct.enc_salt2 = ack->rand_mult();
975  m_ct.enc_keys = ack->tx_enc_keys();
976  }
977 
980  rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
981 
983  json.SetObject();
984 
987 
988  valueI.SetInt(1);
989  json.AddMember("version", valueI, json.GetAllocator());
990 
991  valueS.SetString(m_ct.enc_salt1.c_str(), m_ct.enc_salt1.size());
992  json.AddMember("salt1", valueS, json.GetAllocator());
993 
994  valueS.SetString(m_ct.enc_salt2.c_str(), m_ct.enc_salt2.size());
995  json.AddMember("salt2", valueS, json.GetAllocator());
996 
997  valueS.SetString(m_ct.tx_prefix_hash.c_str(), m_ct.tx_prefix_hash.size());
998  json.AddMember("tx_prefix_hash", valueS, json.GetAllocator());
999 
1000  valueS.SetString(m_ct.enc_keys.c_str(), m_ct.enc_keys.size());
1001  json.AddMember("enc_keys", valueS, json.GetAllocator());
1002 
1003  json.Accept(writer);
1004  return sb.GetString();
1005  }
1006 
1008  {
1010 
1011  // The contents should be JSON if the wallet follows the new format.
1012  if (json.Parse(data.c_str()).HasParseError())
1013  {
1014  throw std::invalid_argument("Data parsing error");
1015  }
1016  else if(!json.IsObject())
1017  {
1018  throw std::invalid_argument("Data parsing error - not an object");
1019  }
1020 
1021  GET_FIELD_FROM_JSON(json, version, int, Int, true, -1);
1024  GET_STRING_FROM_JSON(json, enc_keys, std::string, true, std::string());
1025  GET_STRING_FROM_JSON(json, tx_prefix_hash, std::string, false, std::string());
1026 
1027  if (field_version != 1)
1028  {
1029  throw std::invalid_argument("Unknown version");
1030  }
1031 
1032  res.salt1 = field_salt1;
1033  res.salt2 = field_salt2;
1034  res.tx_enc_keys = field_enc_keys;
1035  res.tx_prefix_hash = field_tx_prefix_hash;
1036  }
1037 
1038  std::shared_ptr<messages::Electroneum::ElectroneumGetTxKeyRequest> get_tx_key(
1039  const hw::device_cold::tx_key_data_t & tx_data)
1040  {
1041  auto req = std::make_shared<messages::Electroneum::ElectroneumGetTxKeyRequest>();
1042  req->set_salt1(tx_data.salt1);
1043  req->set_salt2(tx_data.salt2);
1044  req->set_tx_enc_keys(tx_data.tx_enc_keys);
1045  req->set_tx_prefix_hash(tx_data.tx_prefix_hash);
1046  req->set_reason(0);
1047 
1048  return req;
1049  }
1050 
1052  std::vector<::crypto::secret_key> & tx_keys,
1053  const std::string & tx_prefix_hash,
1054  const ::crypto::secret_key & view_key_priv,
1055  std::shared_ptr<const messages::Electroneum::ElectroneumGetTxKeyAck> ack
1056  )
1057  {
1058  auto enc_key = protocol::tx::compute_enc_key(view_key_priv, tx_prefix_hash, ack->salt());
1059  auto & encrypted_keys = ack->has_tx_derivations() ? ack->tx_derivations() : ack->tx_keys();
1060 
1061  const size_t len_ciphertext = encrypted_keys.size(); // IV || keys || TAG
1063 
1064  size_t keys_len = len_ciphertext - crypto::chacha::IV_SIZE - crypto::chacha::TAG_SIZE;
1065  std::unique_ptr<uint8_t[]> plaintext(new uint8_t[keys_len]);
1066 
1068  encrypted_keys.data() + crypto::chacha::IV_SIZE,
1069  len_ciphertext - crypto::chacha::IV_SIZE,
1070  reinterpret_cast<const uint8_t *>(enc_key.data),
1071  reinterpret_cast<const uint8_t *>(encrypted_keys.data()),
1072  reinterpret_cast<char *>(plaintext.get()), &keys_len);
1073 
1074  CHECK_AND_ASSERT_THROW_MES(keys_len % 32 == 0, "Invalid size");
1075  tx_keys.resize(keys_len / 32);
1076 
1077  for(unsigned i = 0; i < keys_len / 32; ++i)
1078  {
1079  memcpy(tx_keys[i].data, plaintext.get() + 32 * i, 32);
1080  }
1081  memwipe(plaintext.get(), keys_len);
1082  }
1083 
1084 }
1085 }
1086 }
1087 }
const char * res
Definition: hmac_keccak.cpp:41
boost::optional< unsigned > client_version
Definition: device_cold.hpp:49
crypto::public_key pub
std::vector< rct::ecdhTuple > tx_out_ecdh
Definition: protocol.hpp:193
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)
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
Definition: misc_log_ex.h:173
key curveOrder()
Definition: rctOps.h:76
Signer(wallet_shim *wallet2, const unsigned_tx_set *unsigned_tx, size_t tx_idx=0, hw::tx_aux_data *aux_data=nullptr)
Definition: protocol.cpp:380
POD_CLASS ec_point
Definition: crypto.h:70
messages::electroneum::ElectroneumKeyImageSyncStepRequest_ElectroneumTransferDetails ElectroneumTransferDetails
Definition: protocol.hpp:111
void step_set_rsig_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionSetOutputAck > ack)
Definition: protocol.cpp:828
crypto::public_key real_out_tx_key
int crypto_verify_32(const unsigned char *, const unsigned char *)
boost::function< crypto::public_key(const tools::wallet2::transfer_details &td)> get_tx_pub_key_from_received_outs
Definition: device_cold.hpp:41
void apply_permutation(std::vector< size_t > permutation, const F &swap)
void step_init_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionInitAck > ack)
Definition: protocol.cpp:547
crypto::secret_key sec
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
rapidjson::Document json
Definition: transport.hpp:59
#define GET_FIELD_FROM_JSON(json, name, type, jtype, mandatory, def)
Definition: protocol.cpp:51
cryptonote::transaction tx
Definition: protocol.hpp:178
std::vector< std::string > pseudo_outs_hmac
Definition: protocol.hpp:198
void step_permutation_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionInputsPermutationAck > ack)
Definition: protocol.cpp:621
#define GET_STRING_FROM_JSON(json, name, type, mandatory, def)
Definition: protocol.cpp:48
std::shared_ptr< messages::electroneum::ElectroneumTransactionInputViniRequest > step_set_vini_input(size_t idx)
Definition: protocol.cpp:625
::std::string string
Definition: gtest-port.h:1097
void scalarmultKey(key &aP, const key &P, const key &a)
Definition: rctOps.cpp:368
void string_to_key(::crypto::ec_scalar &key, const std::string &str)
Definition: protocol.cpp:96
void step_sign_input_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionSignInputAck > ack)
Definition: protocol.cpp:930
bool bulletproof_VERIFY(const Bulletproof &proof)
key get_pre_mlsag_hash(const rctSig &rv, hw::device &hwdev)
Definition: rctSigs.cpp:403
bool cn_deserialize(const void *buff, size_t len, T &dst)
Definition: protocol.hpp:68
boost::variant< txin_gen, txin_to_script, txin_to_scripthash, txin_to_key, txin_to_key_public > txin_v
std::string cn_serialize(T &obj)
Definition: protocol.hpp:82
bool check_ring_signature(const hash &prefix_hash, const key_image &image, const public_key *const *pubs, std::size_t pubs_count, const signature *sig)
Definition: crypto.h:333
void translate_klrki(ElectroneumMultisigKLRki *dst, const rct::multisig_kLRki *src)
Definition: protocol.cpp:305
const char * key
Definition: hmac_keccak.cpp:39
std::shared_ptr< ElectroneumRsigData > rsig_param
Definition: protocol.hpp:182
crypto namespace.
Definition: crypto.cpp:58
epee::mlocked< tools::scrubbed< ec_scalar > > secret_key
Definition: crypto.h:82
#define BULLETPROOF_MAX_OUTPUTS
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::ElectroneumTransactionSignInputRequest > step_sign_input(size_t idx)
Definition: protocol.cpp:907
void step_all_outs_set_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionAllOutSetAck > ack, hw::device &hwdev)
Definition: protocol.cpp:837
bool rct
void keccak_finish(KECCAK_CTX *ctx, uint8_t *md)
std::vector< std::string > tx_out_entr_hmacs
Definition: protocol.hpp:189
unsigned char uint8_t
Definition: stdint.h:124
std::shared_ptr< messages::electroneum::ElectroneumTransactionSetOutputRequest > step_set_output(size_t idx)
Definition: protocol.cpp:684
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
Definition: protocol.cpp:1038
uint64_t amount
key dest
Definition: rctTypes.h:97
unsigned client_version() const
Definition: protocol.hpp:271
void translate_src_entry(ElectroneumTransactionSourceEntry *dst, const cryptonote::tx_source_entry *src)
Definition: protocol.cpp:285
void step_final_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionFinalAck > ack)
Definition: protocol.cpp:957
size_t real_output
void translate_address(ElectroneumAccountPublicAddress *dst, const cryptonote::account_public_address *src)
Definition: protocol.cpp:272
std::shared_ptr< messages::electroneum::ElectroneumTransactionAllOutSetRequest > step_all_outs_set()
Definition: protocol.cpp:833
std::vector< uint8_t > extra
void assign_to_repeatable(::google::protobuf::RepeatedField< sub_t > *dst, const InputIterator begin, const InputIterator end)
Definition: protocol.hpp:53
std::vector< std::string > tx_in_hmacs
Definition: protocol.hpp:188
void step_set_vini_input_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionInputViniAck > ack)
Definition: protocol.cpp:648
std::vector< cryptonote::tx_source_entry > sources
Definition: wallet2.h:422
std::vector< key > keyV
Definition: rctTypes.h:88
std::string compute_hash(const ElectroneumTransferDetails &rr)
Definition: protocol.cpp:167
std::vector< std::string > spend_encs
Definition: protocol.hpp:196
std::vector< std::string > couts
Definition: protocol.hpp:199
void translate_dst_entry(ElectroneumTransactionDestinationEntry *dst, const cryptonote::tx_destination_entry *src)
Definition: protocol.cpp:277
const crypto::secret_key null_skey
Definition: crypto.cpp:73
void translate_rct_key(ElectroneumRctKey *dst, const rct::ctkey *src)
Definition: protocol.cpp:312
messages::electroneum::ElectroneumTransactionSourceEntry_ElectroneumOutputEntry_ElectroneumRctKeyPublic ElectroneumRctKey
Definition: protocol.hpp:153
messages::electroneum::ElectroneumTransactionSourceEntry_ElectroneumMultisigKLRki ElectroneumMultisigKLRki
Definition: protocol.hpp:151
void load_tx_key_data(hw::device_cold::tx_key_data_t &res, const std::string &data)
Definition: protocol.cpp:1007
string
Definition: rapidjson.h:626
void step_set_input_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionSetInputAck > ack)
Definition: protocol.cpp:563
std::string pod_to_hex(const t_pod_type &s)
Definition: string_tools.h:317
std::shared_ptr< messages::electroneum::ElectroneumTransactionFinalRequest > step_final()
Definition: protocol.cpp:952
GenericValue< UTF8<> > Value
GenericValue with UTF8 encoding.
Definition: document.h:2116
unsigned int uint32_t
Definition: stdint.h:126
std::vector< rct::ctkey > tx_out_pk
Definition: protocol.hpp:192
::crypto::secret_key compute_enc_key(const ::crypto::secret_key &private_view_key, const std::string &aux, const std::string &salt)
Definition: protocol.cpp:351
POD_CLASS ec_scalar
Definition: crypto.h:74
std::string get_varint_data(const T &v)
Returns the string that represents the varint.
Definition: varint.h:85
std::vector< size_t > source_permutation
Definition: protocol.hpp:194
std::vector< uint64_t > grouping_vct
Definition: protocol.hpp:181
std::vector< cryptonote::tx_destination_entry > splitted_dsts
Definition: wallet2.h:424
GenericDocument< UTF8<> > Document
GenericDocument with UTF8 encoding.
Definition: document.h:2512
bool t_serializable_object_to_blob(const t_object &to, blobdata &b_blob)
void keccak_init(KECCAK_CTX *ctx)
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
Definition: device.cpp:38
number
Definition: rapidjson.h:627
uint64_t amount
GenericStringBuffer< UTF8< char >, CrtAllocator > StringBuffer
Definition: fwd.h:59
bool is_subaddress
std::vector< uint8_t > extra
Definition: wallet2.h:426
bool is_integrated
std::vector< std::string > couts_dec
Definition: protocol.hpp:200
POD_CLASS public_key
Definition: crypto.h:76
std::vector< std::string > alphas
Definition: protocol.hpp:195
version
Supported socks variants.
Definition: socks.h:57
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
rct::multisig_kLRki multisig_kLRki
messages::electroneum::ElectroneumTransactionDestinationEntry_ElectroneumAccountPublicAddress ElectroneumAccountPublicAddress
Definition: protocol.hpp:149
std::vector< rct::key > rsig_gamma
Definition: protocol.hpp:201
std::shared_ptr< messages::electroneum::ElectroneumTransactionInitRequest > step_init()
Definition: protocol.cpp:490
account_public_address addr
messages::electroneum::ElectroneumTransactionInitRequest_ElectroneumTransactionData TsxData
Definition: protocol.hpp:147
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
std::shared_ptr< messages::electroneum::ElectroneumTransactionInputsPermutationRequest > step_permutation()
Definition: protocol.cpp:612
void get_transaction_prefix_hash(const transaction_prefix &tx, crypto::hash &h)
unsigned char bytes[32]
Definition: rctTypes.h:86
key identity()
Definition: rctOps.h:73
POD_CLASS hash8
Definition: hash.h:53
std::shared_ptr< messages::electroneum::ElectroneumTransactionAllInputsSetRequest > step_all_inputs_set()
Definition: protocol.cpp:652
std::string original
std::vector< crypto::public_key > real_out_additional_tx_keys
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)
size_t real_output_in_tx_index
crypto::key_image k_image
signed __int64 int64_t
Definition: stdint.h:135
key mask
Definition: rctTypes.h:98
void step_set_output_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionSetOutputAck > ack)
Definition: protocol.cpp:708
void keccak_update(KECCAK_CTX *ctx, const uint8_t *in, size_t inlen)
std::vector< output_entry > outputs
void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen)
void assign_from_repeatable(std::vector< sub_t > *dst, const InputIterator begin, const InputIterator end)
Definition: protocol.hpp:61
RangeProofType range_proof_type
Definition: rctTypes.h:237
Definition: view.h:66
boost::optional< int > bp_version
Definition: device_cold.hpp:48
POD_CLASS hash
Definition: hash.h:50
messages::electroneum::ElectroneumTransactionSourceEntry ElectroneumTransactionSourceEntry
Definition: protocol.hpp:150
bool key_image_data(wallet_shim *wallet, const std::vector< tools::wallet2::transfer_details > &transfers, std::vector< ElectroneumTransferDetails > &res)
Definition: protocol.cpp:145
std::vector< cryptonote::address_parse_info > tx_recipients
Definition: device_cold.hpp:47
std::string to_string(t_connection_type type)
messages::electroneum::ElectroneumTransactionDestinationEntry ElectroneumTransactionDestinationEntry
Definition: protocol.hpp:148
std::string hash_addr(const ElectroneumAccountPublicAddress *addr, boost::optional< uint64_t > amount, boost::optional< bool > is_subaddr)
Definition: protocol.cpp:317
std::vector< crypto::public_key > get_additional_tx_pub_keys_from_extra(const std::vector< uint8_t > &tx_extra)
void * memwipe(void *src, size_t n)
std::shared_ptr< rct::rctSig > rv
Definition: protocol.hpp:207
std::vector< std::string > tx_out_hmacs
Definition: protocol.hpp:190
std::shared_ptr< messages::electroneum::ElectroneumTransactionSetInputRequest > step_set_input(size_t idx)
Definition: protocol.cpp:555
std::string key_to_string(const ::crypto::ec_point &key)
Definition: protocol.cpp:80
std::shared_ptr< messages::electroneum::ElectroneumTransactionSetOutputRequest > step_rsig(size_t idx)
Definition: protocol.cpp:813
tx_construction_data tx_data
Definition: protocol.hpp:177
std::vector< rsig_v > tx_out_rsigs
Definition: protocol.hpp:191
void hmac_keccak_hash(uint8_t *out, const uint8_t *key, size_t keylen, const uint8_t *in, size_t inlen)
void step_all_inputs_set_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionAllInputsSetAck > ack)
Definition: protocol.cpp:656
Bulletproof bulletproof_PROVE(const rct::key &v, const rct::key &gamma)
std::vector< std::string > pseudo_outs
Definition: protocol.hpp:197
rct::key mask
rct::keyV V
Definition: rctTypes.h:181