Electroneum
Loading...
Searching...
No Matches
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
76namespace hw{
77namespace trezor{
78namespace protocol{
79
80 std::string key_to_string(const ::crypto::ec_point & key){
81 return std::string(key.data, sizeof(key.data));
82 }
83
84 std::string key_to_string(const ::crypto::ec_scalar & key){
85 return std::string(key.data, sizeof(key.data));
86 }
87
88 std::string key_to_string(const ::crypto::hash & key){
89 return std::string(key.data, sizeof(key.data));
90 }
91
92 std::string key_to_string(const ::rct::key & key){
93 return std::string(reinterpret_cast<const char*>(key.bytes), sizeof(key.bytes));
94 }
95
96 void string_to_key(::crypto::ec_scalar & key, const std::string & str){
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
103 void string_to_key(::crypto::ec_point & key, const std::string & str){
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
117namespace crypto {
118namespace 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){
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
143namespace 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
224 void live_refresh_ack(const ::crypto::secret_key & view_key_priv,
225 const ::crypto::public_key& out_key,
226 const std::shared_ptr<messages::Electroneum::ElectroneumLiveRefreshStepAck> & ack,
227 ::cryptonote::keypair& in_ephemeral,
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");
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
262 CHECK_AND_ASSERT_THROW_MES(::crypto::check_ring_signature((const ::crypto::hash&)ki, ki, pkeys, &sig),
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
270namespace 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
351 ::crypto::secret_key compute_enc_key(const ::crypto::secret_key & private_view_key, const std::string & aux, const std::string & salt)
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){
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
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
979 rapidjson::StringBuffer sb;
980 rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
981
982 rapidjson::Document json;
983 json.SetObject();
984
985 rapidjson::Value valueS(rapidjson::kStringType);
986 rapidjson::Value valueI(rapidjson::kNumberType);
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
1007 void load_tx_key_data(hw::device_cold::tx_key_data_t & res, const std::string & data)
1008 {
1009 rapidjson::Document json;
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);
1022 GET_STRING_FROM_JSON(json, salt1, std::string, true, std::string());
1023 GET_STRING_FROM_JSON(json, salt2, std::string, true, std::string());
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}
uint8_t version
std::shared_ptr< messages::electroneum::ElectroneumTransactionSignInputRequest > step_sign_input(size_t idx)
Definition protocol.cpp:907
void step_set_vini_input_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionInputViniAck > ack)
Definition protocol.cpp:648
void step_all_outs_set_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionAllOutSetAck > ack, hw::device &hwdev)
Definition protocol.cpp:837
std::shared_ptr< messages::electroneum::ElectroneumTransactionFinalRequest > step_final()
Definition protocol.cpp:952
std::shared_ptr< messages::electroneum::ElectroneumTransactionInitRequest > step_init()
Definition protocol.cpp:490
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
void step_set_input_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionSetInputAck > ack)
Definition protocol.cpp:563
void step_set_rsig_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionSetOutputAck > ack)
Definition protocol.cpp:828
void step_init_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionInitAck > ack)
Definition protocol.cpp:547
void step_sign_input_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionSignInputAck > ack)
Definition protocol.cpp:930
std::shared_ptr< messages::electroneum::ElectroneumTransactionSetOutputRequest > step_set_output(size_t idx)
Definition protocol.cpp:684
std::shared_ptr< messages::electroneum::ElectroneumTransactionSetOutputRequest > step_rsig(size_t idx)
Definition protocol.cpp:813
std::shared_ptr< messages::electroneum::ElectroneumTransactionInputsPermutationRequest > step_permutation()
Definition protocol.cpp:612
void step_final_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionFinalAck > ack)
Definition protocol.cpp:957
void step_set_output_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionSetOutputAck > ack)
Definition protocol.cpp:708
std::shared_ptr< messages::electroneum::ElectroneumTransactionSetInputRequest > step_set_input(size_t idx)
Definition protocol.cpp:555
void step_all_inputs_set_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionAllInputsSetAck > ack)
Definition protocol.cpp:656
void step_permutation_ack(std::shared_ptr< const messages::electroneum::ElectroneumTransactionInputsPermutationAck > ack)
Definition protocol.cpp:621
std::shared_ptr< messages::electroneum::ElectroneumTransactionAllOutSetRequest > step_all_outs_set()
Definition protocol.cpp:833
std::shared_ptr< messages::electroneum::ElectroneumTransactionAllInputsSetRequest > step_all_inputs_set()
Definition protocol.cpp:652
std::shared_ptr< messages::electroneum::ElectroneumTransactionInputViniRequest > step_set_vini_input(size_t idx)
Definition protocol.cpp:625
tx_construction_data tx_data
Definition protocol.hpp:177
std::vector< uint64_t > grouping_vct
Definition protocol.hpp:181
std::vector< rct::key > rsig_gamma
Definition protocol.hpp:201
std::vector< rsig_v > tx_out_rsigs
Definition protocol.hpp:191
std::vector< rct::ctkey > tx_out_pk
Definition protocol.hpp:192
int crypto_verify_32(const unsigned char *, const unsigned char *)
#define BULLETPROOF_MAX_OUTPUTS
void * memcpy(void *a, const void *b, size_t c)
void hmac_keccak_hash(uint8_t *out, const uint8_t *key, size_t keylen, const uint8_t *in, size_t inlen)
const char * res
const char * key
void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen)
void keccak_finish(KECCAK_CTX *ctx, uint8_t *md)
void keccak_update(KECCAK_CTX *ctx, const uint8_t *in, size_t inlen)
void keccak_init(KECCAK_CTX *ctx)
void * memwipe(void *src, size_t n)
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
crypto namespace.
Definition crypto.cpp:58
POD_CLASS ec_point
Definition crypto.h:70
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 public_key
Definition crypto.h:79
POD_CLASS key_image
Definition crypto.h:105
POD_CLASS ec_scalar
Definition crypto.h:74
POD_CLASS hash
Definition hash.h:50
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
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
std::vector< crypto::public_key > get_additional_tx_pub_keys_from_extra(const std::vector< uint8_t > &tx_extra)
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
void get_transaction_prefix_hash(const transaction_prefix &tx, crypto::hash &h)
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
bool t_serializable_object_to_blob(const t_object &to, blobdata &b_blob)
std::string pod_to_hex(const t_pod_type &s)
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
messages::electroneum::ElectroneumKeyImageSyncStepRequest_ElectroneumTransferDetails ElectroneumTransferDetails
Definition protocol.hpp:111
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
std::string compute_hash(const ElectroneumTransferDetails &rr)
Definition protocol.cpp:167
void load_tx_key_data(hw::device_cold::tx_key_data_t &res, const std::string &data)
messages::electroneum::ElectroneumTransactionSourceEntry_ElectroneumOutputEntry_ElectroneumRctKeyPublic ElectroneumRctKey
Definition protocol.hpp:153
void translate_klrki(ElectroneumMultisigKLRki *dst, const rct::multisig_kLRki *src)
Definition protocol.cpp:305
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
messages::electroneum::ElectroneumTransactionInitRequest_ElectroneumTransactionData TsxData
Definition protocol.hpp:147
std::string hash_addr(const ElectroneumAccountPublicAddress *addr, boost::optional< uint64_t > amount, boost::optional< bool > is_subaddr)
Definition protocol.cpp:317
messages::electroneum::ElectroneumTransactionSourceEntry ElectroneumTransactionSourceEntry
Definition protocol.hpp:150
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)
::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
messages::electroneum::ElectroneumTransactionDestinationEntry ElectroneumTransactionDestinationEntry
Definition protocol.hpp:148
void translate_src_entry(ElectroneumTransactionSourceEntry *dst, const cryptonote::tx_source_entry *src)
Definition protocol.cpp:285
void translate_dst_entry(ElectroneumTransactionDestinationEntry *dst, const cryptonote::tx_destination_entry *src)
Definition protocol.cpp:277
messages::electroneum::ElectroneumTransactionSourceEntry_ElectroneumMultisigKLRki ElectroneumMultisigKLRki
Definition protocol.hpp:151
void translate_rct_key(ElectroneumRctKey *dst, const rct::ctkey *src)
Definition protocol.cpp:312
tools::wallet2::unsigned_tx_set unsigned_tx_set
Definition protocol.hpp:157
void translate_address(ElectroneumAccountPublicAddress *dst, const cryptonote::account_public_address *src)
Definition protocol.cpp:272
messages::electroneum::ElectroneumTransactionDestinationEntry_ElectroneumAccountPublicAddress ElectroneumAccountPublicAddress
Definition protocol.hpp:149
void assign_from_repeatable(std::vector< sub_t > *dst, const InputIterator begin, const InputIterator end)
Definition protocol.hpp:61
bool cn_deserialize(const void *buff, size_t len, T &dst)
Definition protocol.hpp:68
void string_to_key(::crypto::ec_scalar &key, const std::string &str)
Definition protocol.cpp:96
void assign_to_repeatable(::google::protobuf::RepeatedField< sub_t > *dst, const InputIterator begin, const InputIterator end)
Definition protocol.hpp:53
std::string key_to_string(const ::crypto::ec_point &key)
Definition protocol.cpp:80
std::string cn_serialize(T &obj)
Definition protocol.hpp:82
rapidjson::Document json
Definition transport.hpp:59
Definition device.cpp:38
key curveOrder()
Definition rctOps.h:76
bool bulletproof_VERIFY(const Bulletproof &proof)
std::vector< key > keyV
Definition rctTypes.h:88
key get_pre_mlsag_hash(const rctSig &rv, hw::device &hwdev)
Definition rctSigs.cpp:403
void scalarmultKey(key &aP, const key &P, const key &a)
Definition rctOps.cpp:368
@ RangeProofBorromean
Definition rctTypes.h:235
@ RangeProofMultiOutputBulletproof
Definition rctTypes.h:235
@ RangeProofBulletproof
Definition rctTypes.h:235
@ RangeProofPaddedBulletproof
Definition rctTypes.h:235
key identity()
Definition rctOps.h:73
std::string get_varint_data(const T &v)
Returns the string that represents the varint.
Definition varint.h:85
void apply_permutation(std::vector< size_t > permutation, const F &swap)
#define GET_STRING_FROM_JSON(json, name, type, mandatory, def)
Definition protocol.cpp:48
#define GET_FIELD_FROM_JSON(json, name, type, jtype, mandatory, def)
Definition protocol.cpp:51
signed __int64 int64_t
Definition stdint.h:135
unsigned int uint32_t
Definition stdint.h:126
unsigned char uint8_t
Definition stdint.h:124
crypto::secret_key sec
crypto::public_key pub
bool is_subaddress
bool is_integrated
std::string original
uint64_t amount
account_public_address addr
std::vector< crypto::public_key > real_out_additional_tx_keys
crypto::public_key real_out_tx_key
uint64_t amount
rct::multisig_kLRki multisig_kLRki
bool rct
size_t real_output
std::vector< output_entry > outputs
rct::key mask
size_t real_output_in_tx_index
boost::function< crypto::public_key(const tools::wallet2::transfer_details &td)> get_tx_pub_key_from_received_outs
rct::keyV V
Definition rctTypes.h:181
RangeProofType range_proof_type
Definition rctTypes.h:237
unsigned char bytes[32]
Definition rctTypes.h:86
std::vector< uint8_t > extra
Definition wallet2.h:426
std::vector< cryptonote::tx_destination_entry > splitted_dsts
Definition wallet2.h:424
Definition view.h:66