diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index 274a2f25..29aa7483 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -386,8 +386,14 @@ namespace currency }; // applicable flags for tx_service_attachment::flags, can be combined using bitwise OR -#define TX_SERVICE_ATTACHMENT_ENCRYPT_BODY static_cast(1 << 0) -#define TX_SERVICE_ATTACHMENT_DEFLATE_BODY static_cast(1 << 1) +#define TX_SERVICE_ATTACHMENT_ENCRYPT_BODY static_cast(1 << 0) +#define TX_SERVICE_ATTACHMENT_DEFLATE_BODY static_cast(1 << 1) + +// with this flag enabled body encrypted/decrypted with the key created as a derivation from onetime key and "spend keys" of receiver +#define TX_SERVICE_ATTACHMENT_ENCRYPT_BODY_ISOLATE_AUDITABLE static_cast(1 << 2) +// add proof of content, without revealing secrete +#define TX_SERVICE_ATTACHMENT_ENCRYPT_ADD_PROOF static_cast(1 << 3) + //, diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 7c014f2a..0d877864 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -759,8 +759,12 @@ namespace currency struct encrypt_attach_visitor : public boost::static_visitor { bool& m_was_crypted_entries; + const keypair& m_onetime_keypair; + const account_public_address& m_destination_addr; const crypto::key_derivation& m_key; - encrypt_attach_visitor(bool& was_crypted_entries, const crypto::key_derivation& key) :m_was_crypted_entries(was_crypted_entries), m_key(key) + + encrypt_attach_visitor(bool& was_crypted_entries, const crypto::key_derivation& key, const keypair& onetime_keypair = keypair(), const account_public_address& destination_addr = account_public_address()) : + m_was_crypted_entries(was_crypted_entries), m_key(key), m_onetime_keypair(onetime_keypair), m_destination_addr(m_destination_addr) {} void operator()(tx_comment& comment) { @@ -789,6 +793,7 @@ namespace currency } void operator()(tx_service_attachment& sa) { + const std::string orignal_body = sa.body; if (sa.flags&TX_SERVICE_ATTACHMENT_DEFLATE_BODY) { zlib_helper::pack(sa.body); @@ -796,7 +801,27 @@ namespace currency if (sa.flags&TX_SERVICE_ATTACHMENT_ENCRYPT_BODY) { - crypto::chacha_crypt(sa.body, m_key); + crypto::key_derivation derivation_local = m_key; + if (sa.flags&TX_SERVICE_ATTACHMENT_ENCRYPT_BODY_ISOLATE_AUDITABLE) + { + CHECK_AND_ASSERT_THROW_MES(m_destination_addr.spend_public_key != currency::null_pkey && m_onetime_keypair.sec != currency::null_skey, "tx_service_attachment with TX_SERVICE_ATTACHMENT_ENCRYPT_BODY_ISOLATE_AUDITABLE: keys uninitialized"); + //encrypt with "spend keys" only, to prevent auditable watchers decrypt it + bool r = crypto::generate_key_derivation(m_destination_addr.spend_public_key, m_onetime_keypair.sec, derivation_local); + crypto::chacha_crypt(sa.body, derivation_local); + } + else + { + crypto::chacha_crypt(sa.body, derivation_local); + } + if (sa.flags&TX_SERVICE_ATTACHMENT_ENCRYPT_ADD_PROOF) + { + //take hash from derivation and use it as a salt + crypto::hash derivation_hash = crypto::cn_fast_hash(&derivation_local, sizeof(derivation_local)); + std::string salted_body = orignal_body; + string_tools::apped_pod_to_strbuff(salted_body, derivation_hash); + crypto::hash proof_hash = crypto::cn_fast_hash(salted_body.data(), salted_body.size()); + sa.security.push_back(*(crypto::public_key*)&proof_hash); + } m_was_crypted_entries = true; } } @@ -808,12 +833,18 @@ namespace currency struct decrypt_attach_visitor : public boost::static_visitor { + const account_keys& m_acc_keys; + const crypto::public_key& m_tx_onetime_pubkey; const crypto::key_derivation& rkey; std::vector& rdecrypted_att; decrypt_attach_visitor(const crypto::key_derivation& key, - std::vector& decrypted_att) : + std::vector& decrypted_att, + const account_keys& acc_keys = account_keys(), + const crypto::public_key& tx_onetime_pubkey = crypto::public_key()) : rkey(key), - rdecrypted_att(decrypted_att) + rdecrypted_att(decrypted_att), + m_acc_keys(acc_keys), + m_tx_onetime_pubkey(tx_onetime_pubkey) {} void operator()(const tx_comment& comment) { @@ -825,15 +856,44 @@ namespace currency void operator()(const tx_service_attachment& sa) { tx_service_attachment local_sa = sa; + crypto::key_derivation derivation_local = rkey; if (sa.flags&TX_SERVICE_ATTACHMENT_ENCRYPT_BODY) { - crypto::chacha_crypt(local_sa.body, rkey); + if (sa.flags&TX_SERVICE_ATTACHMENT_ENCRYPT_BODY_ISOLATE_AUDITABLE) + { + if (m_acc_keys.spend_secret_key == null_skey) + { + //this watch only wallet, decrypting supposed to be impossible + return; + } + CHECK_AND_ASSERT_THROW_MES(m_acc_keys.spend_secret_key != currency::null_skey, "tx_service_attachment with TX_SERVICE_ATTACHMENT_ENCRYPT_BODY_ISOLATE_AUDITABLE: keys uninitialized"); + bool r = crypto::generate_key_derivation(m_tx_onetime_pubkey, m_acc_keys.spend_secret_key, derivation_local); + CHECK_AND_ASSERT_THROW_MES(r, "Failed to generate_key_derivation at TX_SERVICE_ATTACHMENT_ENCRYPT_BODY_ISOLATE_AUDITABLE"); + crypto::chacha_crypt(sa.body, derivation_local); + + } + else + { + crypto::chacha_crypt(local_sa.body, derivation_local); + } } if (sa.flags&TX_SERVICE_ATTACHMENT_DEFLATE_BODY) { zlib_helper::unpack(local_sa.body); } + + if (sa.flags&TX_SERVICE_ATTACHMENT_ENCRYPT_BODY && sa.flags&TX_SERVICE_ATTACHMENT_ENCRYPT_ADD_PROOF) + { + CHECK_AND_ASSERT_MES(sa.security.size() == 1, void(), "Unexpected key in tx_service_attachment with TX_SERVICE_ATTACHMENT_ENCRYPT_BODY_ISOLATE_AUDITABLE"); + //take hash from derivation and use it as a salt + crypto::hash derivation_hash = crypto::cn_fast_hash(&derivation_local, sizeof(derivation_local)); + std::string salted_body = local_sa.body; + string_tools::apped_pod_to_strbuff(salted_body, derivation_hash); + crypto::hash proof_hash = crypto::cn_fast_hash(salted_body.data(), salted_body.size()); + CHECK_AND_ASSERT_MES(*(crypto::public_key*)&proof_hash == sa.security.front(), void(), "Proof hash missmatch on decrypting with TX_SERVICE_ATTACHMENT_ENCRYPT_ADD_PROOF"); + } + rdecrypted_att.push_back(local_sa); } @@ -870,9 +930,10 @@ namespace currency //--------------------------------------------------------------- template - bool decrypt_payload_items(const crypto::key_derivation& derivation, const items_container_t& items_to_decrypt, std::vector& decrypted_att) + bool decrypt_payload_items(const crypto::key_derivation& derivation, const items_container_t& items_to_decrypt, std::vector& decrypted_att, const account_keys& acc_keys = account_keys(), + const crypto::public_key& tx_onetime_pubkey = crypto::public_key()) { - decrypt_attach_visitor v(derivation, decrypted_att); + decrypt_attach_visitor v(derivation, decrypted_att, acc_keys, tx_onetime_pubkey); for (auto& a : items_to_decrypt) boost::apply_visitor(v, a); @@ -955,8 +1016,8 @@ namespace currency return true; } - decrypt_payload_items(derivation, tx.extra, decrypted_items); - decrypt_payload_items(derivation, tx.attachment, decrypted_items); + decrypt_payload_items(derivation, tx.extra, decrypted_items, acc_keys, get_tx_pub_key_from_extra(tx)); + decrypt_payload_items(derivation, tx.attachment, decrypted_items, acc_keys, get_tx_pub_key_from_extra(tx)); return true; } @@ -969,11 +1030,11 @@ namespace currency bool was_attachment_crypted_entries = false; bool was_extra_crypted_entries = false; - encrypt_attach_visitor v(was_attachment_crypted_entries, derivation); + encrypt_attach_visitor v(was_attachment_crypted_entries, derivation, tx_random_key, destination_addr); for (auto& a : tx.attachment) boost::apply_visitor(v, a); - encrypt_attach_visitor v2(was_extra_crypted_entries, derivation); + encrypt_attach_visitor v2(was_extra_crypted_entries, derivation, tx_random_key, destination_addr); for (auto& a : tx.extra) boost::apply_visitor(v2, a); @@ -2888,6 +2949,13 @@ namespace currency return tools::base58::encode_addr(CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX, t_serializable_object_to_blob(addr)); // new format Zano address (normal) } //----------------------------------------------------------------------- + bool is_address_looks_like_wrapped(const std::string& addr) + { + if (addr.length() == 42 && addr.substr(0, 2) == "0x") + return true; + else return false; + } + //----------------------------------------------------------------------- std::string get_account_address_and_payment_id_as_str(const account_public_address& addr, const payment_id_t& payment_id) { if (addr.flags == 0) diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 6474307d..86952896 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -289,6 +289,7 @@ namespace currency bool decrypt_payload_items(bool is_income, const transaction& tx, const account_keys& acc_keys, std::vector& decrypted_items); void encrypt_attachments(transaction& tx, const account_keys& sender_keys, const account_public_address& destination_addr, const keypair& tx_random_key); bool is_derivation_used_to_encrypt(const transaction& tx, const crypto::key_derivation& derivation); + bool is_address_looks_like_wrapped(const std::string& addr); void load_wallet_transfer_info_flags(tools::wallet_public::wallet_transfer_info& x); uint64_t get_tx_type(const transaction& tx); uint64_t get_tx_type_ex(const transaction& tx, tx_out& htlc_out, txin_htlc& htlc_in); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 8656eb2c..3517e318 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -20,6 +20,7 @@ #include "wallet/wallet_rpc_server.h" #include "version.h" #include "string_coding.h" +#include "wallet/wrap_service.h" #include @@ -1208,13 +1209,32 @@ bool simple_wallet::transfer(const std::vector &args_) return true; } + std::vector extra; vector dsts; + bool wrapped_transaction = false; for (size_t i = 0; i < local_args.size(); i += 2) { std::string integrated_payment_id; currency::tx_destination_entry de; de.addr.resize(1); - if(!(de.addr.size() == 1 && m_wallet->get_transfer_address(local_args[i], de.addr.front(), integrated_payment_id))) + //check if address looks like wrapped address + if (is_address_looks_like_wrapped(local_args[i])) + { + success_msg_writer(true) << "Address " << local_args[i] << " recognized as wrapped address, creating wrapping transaction..."; + //put into service attachment specially encrypted entry which will contain wrap address and network + tx_service_attachment sa = AUTO_VAL_INIT(sa); + sa.service_id = BC_WRAP_SERVICE_ID; + sa.instruction = BC_WRAP_SERVICE_INSTRUCTION_ERC20; + sa.flags = TX_SERVICE_ATTACHMENT_ENCRYPT_BODY | TX_SERVICE_ATTACHMENT_ENCRYPT_BODY_ISOLATE_AUDITABLE; + sa.body = local_args[i]; + extra.push_back(sa); + + currency::account_public_address acc = AUTO_VAL_INIT(acc); + currency::get_account_address_from_str(acc, BC_WRAP_SERVICE_CUSTODY_WALLET); + de.addr.front() = acc; + wrapped_transaction = true; + //encrypt body with a special way + }else if(!(de.addr.size() == 1 && m_wallet->get_transfer_address(local_args[i], de.addr.front(), integrated_payment_id))) { fail_msg_writer() << "wrong address: " << local_args[i]; return true; @@ -1258,13 +1278,19 @@ bool simple_wallet::transfer(const std::vector &args_) try { currency::transaction tx; - std::vector extra; m_wallet->transfer(dsts, fake_outs_count, 0, m_wallet->get_core_runtime_config().tx_default_fee, extra, attachments, tx); if (!m_wallet->is_watch_only()) - success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx) << ", " << get_object_blobsize(tx) << " bytes"; + { + if(wrapped_transaction) + success_msg_writer(true) << "Money successfully sent to wZano custody wallet, transaction " << get_transaction_hash(tx) << ", " << get_object_blobsize(tx) << " bytes"; + else + success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx) << ", " << get_object_blobsize(tx) << " bytes"; + } else + { success_msg_writer(true) << "Transaction prepared for signing and saved into \"zano_tx_unsigned\" file, use full wallet to sign transfer and then use \"submit_transfer\" on this wallet to broadcast the transaction to the network"; + } } catch (const tools::error::daemon_busy&) { diff --git a/src/wallet/wrap_service.h b/src/wallet/wrap_service.h new file mode 100644 index 00000000..852d60d7 --- /dev/null +++ b/src/wallet/wrap_service.h @@ -0,0 +1,12 @@ +// Copyright (c) 2014-2018 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#define BC_WRAP_SERVICE_ID "W" + +#define BC_WRAP_SERVICE_INSTRUCTION_ERC20 "ERC20" //erc20 wrapped operation + + +#define BC_WRAP_SERVICE_CUSTODY_WALLET "aZxbJPXzkjCJDpGEVvkMir9B4fRKPo73r2e5D7nLHuVgEBXXQYc2Tk2hHroxVwiCDLDHZu215pgNocUsrchH4HHzWbHzL4nMfPq" \ No newline at end of file