aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cryptonote_core/cryptonote_format_utils.cpp122
-rw-r--r--src/cryptonote_core/cryptonote_format_utils.h7
-rw-r--r--src/cryptonote_core/tx_extra.h1
-rw-r--r--src/simplewallet/simplewallet.cpp10
-rw-r--r--src/wallet/wallet2.cpp22
-rw-r--r--src/wallet/wallet_rpc_server.cpp11
-rw-r--r--src/wallet/wallet_rpc_server.h2
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h4
8 files changed, 164 insertions, 15 deletions
diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp
index 1b21894a0..a79c3cdd3 100644
--- a/src/cryptonote_core/cryptonote_format_utils.cpp
+++ b/src/cryptonote_core/cryptonote_format_utils.cpp
@@ -38,6 +38,8 @@ using namespace epee;
#include "crypto/crypto.h"
#include "crypto/hash.h"
+#define ENCRYPTED_PAYMENT_ID_TAIL 0x8d
+
namespace cryptonote
{
//---------------------------------------------------------------
@@ -303,24 +305,92 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
- void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id)
+ bool remove_extra_nonce_tx_extra(std::vector<uint8_t>& tx_extra)
+ {
+ std::string extra_str(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size());
+ std::istringstream iss(extra_str);
+ binary_archive<false> ar(iss);
+ std::ostringstream oss;
+ binary_archive<true> newar(oss);
+
+ bool eof = false;
+ while (!eof)
+ {
+ tx_extra_field field;
+ bool r = ::do_serialize(ar, field);
+ CHECK_AND_NO_ASSERT_MES(r, false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size())));
+ if (field.type() != typeid(tx_extra_nonce))
+ ::do_serialize(newar, field);
+
+ std::ios_base::iostate state = iss.rdstate();
+ eof = (EOF == iss.peek());
+ iss.clear(state);
+ }
+ CHECK_AND_NO_ASSERT_MES(::serialization::check_stream_state(ar), false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size())));
+ tx_extra.clear();
+ std::string s = oss.str();
+ tx_extra.reserve(s.size());
+ std::copy(s.begin(), s.end(), std::back_inserter(tx_extra));
+ return true;
+ }
+ //---------------------------------------------------------------
+ void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id, bool encrypted)
{
extra_nonce.clear();
- extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID);
+ extra_nonce.push_back(encrypted ? TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID : TX_EXTRA_NONCE_PAYMENT_ID);
const uint8_t* payment_id_ptr = reinterpret_cast<const uint8_t*>(&payment_id);
std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce));
}
//---------------------------------------------------------------
- bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id)
+ bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id, bool &encrypted)
{
if(sizeof(crypto::hash) + 1 != extra_nonce.size())
return false;
- if(TX_EXTRA_NONCE_PAYMENT_ID != extra_nonce[0])
+ if(TX_EXTRA_NONCE_PAYMENT_ID != extra_nonce[0] && TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID != extra_nonce[0])
return false;
payment_id = *reinterpret_cast<const crypto::hash*>(extra_nonce.data() + 1);
+ encrypted = TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID == extra_nonce[0];
return true;
}
//---------------------------------------------------------------
+ crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations)
+ {
+ if (destinations.empty())
+ return null_pkey;
+ for (size_t n = 1; n < destinations.size(); ++n)
+ {
+ if (!memcmp(&destinations[n].addr, &sender_keys.m_account_address, sizeof(destinations[0].addr)))
+ continue;
+ if (memcmp(&destinations[n].addr, &destinations[0].addr, sizeof(destinations[0].addr)))
+ return null_pkey;
+ }
+ return destinations[0].addr.m_view_public_key;
+ }
+ //---------------------------------------------------------------
+ bool encrypt_payment_id(crypto::hash &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key)
+ {
+ crypto::key_derivation derivation;
+ crypto::hash hash;
+ char data[33]; /* A hash, and an extra byte */
+
+ if (!generate_key_derivation(public_key, secret_key, derivation))
+ return false;
+
+ memcpy(data, &derivation, 32);
+ data[32] = ENCRYPTED_PAYMENT_ID_TAIL;
+ cn_fast_hash(data, 33, hash);
+
+ for (size_t b = 0; b < 32; ++b)
+ payment_id.data[b] ^= hash.data[b];
+
+ return true;
+ }
+ bool decrypt_payment_id(crypto::hash &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key)
+ {
+ // Encryption and decryption are the same operation (xor with a key)
+ return encrypt_payment_id(payment_id, public_key, secret_key);
+ }
+ //---------------------------------------------------------------
bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time)
{
tx.vin.clear();
@@ -334,6 +404,50 @@ namespace cryptonote
keypair txkey = keypair::generate();
add_tx_pub_key_to_extra(tx, txkey.pub);
+ // if we have a stealth payment id, find it and encrypt it with the tx key now
+ std::vector<tx_extra_field> tx_extra_fields;
+ if (parse_tx_extra(tx.extra, tx_extra_fields))
+ {
+ tx_extra_nonce extra_nonce;
+ if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
+ {
+ crypto::hash payment_id = null_hash;
+ bool encrypted;
+ if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id, encrypted) && encrypted)
+ {
+ LOG_PRINT_L2("Encrypting payment id " << payment_id);
+ crypto::key_derivation derivation;
+ crypto::public_key view_key_pub = get_destination_view_key_pub(destinations);
+ if (view_key_pub == null_pkey)
+ {
+ LOG_ERROR("Destinations have to have exactly one output to support encrypted payment ids");
+ return false;
+ }
+
+ if (!encrypt_payment_id(payment_id, view_key_pub, txkey.sec))
+ {
+ LOG_ERROR("Failed to encrypt payment id");
+ return false;
+ }
+
+ std::string extra_nonce;
+ set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id, true);
+ remove_extra_nonce_tx_extra(tx.extra);
+ if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
+ {
+ LOG_ERROR("Failed to add encrypted payment id to tx extra");
+ return false;
+ }
+ LOG_PRINT_L1("Encrypted payment ID: " << payment_id);
+ }
+ }
+ }
+ else
+ {
+ LOG_ERROR("Failed to parse tx extra");
+ return false;
+ }
+
struct input_generation_context_data
{
keypair in_ephemeral;
diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h
index fd785aafd..69baa20cf 100644
--- a/src/cryptonote_core/cryptonote_format_utils.h
+++ b/src/cryptonote_core/cryptonote_format_utils.h
@@ -45,6 +45,8 @@ namespace cryptonote
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx);
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 1);
+ bool encrypt_payment_id(crypto::hash &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key);
+ bool decrypt_payment_id(crypto::hash &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key);
struct tx_source_entry
{
@@ -85,8 +87,9 @@ namespace cryptonote
crypto::public_key get_tx_pub_key_from_extra(const transaction& tx);
bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key);
bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce);
- void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id);
- bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id);
+ bool remove_extra_nonce_tx_extra(std::vector<uint8_t>& tx_extra);
+ void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id, bool encrypted);
+ bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id, bool &encrypted);
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index);
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered);
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered);
diff --git a/src/cryptonote_core/tx_extra.h b/src/cryptonote_core/tx_extra.h
index ccfe4d1d9..012f41593 100644
--- a/src/cryptonote_core/tx_extra.h
+++ b/src/cryptonote_core/tx_extra.h
@@ -40,6 +40,7 @@
#define TX_EXTRA_MERGE_MINING_TAG 0x03
#define TX_EXTRA_NONCE_PAYMENT_ID 0x00
+#define TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID 0x01
namespace cryptonote
{
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index cad83bb45..9c2396029 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -1288,6 +1288,7 @@ bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::str
std::vector<uint8_t> extra;
bool payment_id_seen = false;
+ bool encrypt_payment_id = false;
if (1 == local_args.size() % 2)
{
std::string payment_id_str = local_args.back();
@@ -1298,7 +1299,7 @@ bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::str
if(r)
{
std::string extra_nonce;
- set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
+ set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id, false);
r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
}
@@ -1376,6 +1377,11 @@ bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::str
return true;
}
}
+ else
+ {
+ if (has_payment_id)
+ encrypt_payment_id = true;
+ }
if (has_payment_id) {
if (payment_id_seen && payment_id != new_payment_id) {
@@ -1386,7 +1392,7 @@ bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::str
if (!payment_id_seen)
{
std::string extra_nonce;
- set_payment_id_to_tx_extra_nonce(extra_nonce, new_payment_id);
+ set_payment_id_to_tx_extra_nonce(extra_nonce, new_payment_id, encrypt_payment_id);
bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
if(!r)
{
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 5b7b19520..12db73423 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -150,6 +150,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
process_unconfirmed(tx);
std::vector<size_t> outs;
uint64_t tx_money_got_in_outs = 0;
+ crypto::public_key tx_pub_key = null_pkey;
std::vector<tx_extra_field> tx_extra_fields;
if(!parse_tx_extra(tx.extra, tx_extra_fields))
@@ -170,7 +171,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
return;
}
- crypto::public_key tx_pub_key = pub_key_field.pub_key;
+ tx_pub_key = pub_key_field.pub_key;
bool r = lookup_acc_outs(m_account.get_keys(), tx, tx_pub_key, outs, tx_money_got_in_outs);
THROW_WALLET_EXCEPTION_IF(!r, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
@@ -236,9 +237,26 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
crypto::hash payment_id = null_hash;
if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
{
- if(get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
+ bool encrypted;
+ if(get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id, encrypted) && encrypted)
{
// We got a payment ID to go with this tx
+ LOG_PRINT_L2("Found encrypted payment ID: " << payment_id);
+ if (tx_pub_key != null_pkey)
+ {
+ if (!decrypt_payment_id(payment_id, tx_pub_key, m_account.get_keys().m_view_secret_key))
+ {
+ LOG_PRINT_L0("Failed to decrypt payment ID: " << payment_id);
+ }
+ else
+ {
+ LOG_PRINT_L2("Decrypted payment ID: " << payment_id);
+ }
+ }
+ else
+ {
+ LOG_PRINT_L1("No public key found in tx, unable to decrypt payment id");
+ }
}
}
uint64_t received = (tx_money_spent_in_ins < tx_money_got_in_outs) ? tx_money_got_in_outs - tx_money_spent_in_ins : 0;
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 1d38695ae..4797f76a2 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -117,7 +117,7 @@ namespace tools
}
//------------------------------------------------------------------------------------------------------------------------------
- bool wallet_rpc_server::validate_transfer(const std::list<wallet_rpc::transfer_destination> destinations, std::string payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er)
+ bool wallet_rpc_server::validate_transfer(const std::list<wallet_rpc::transfer_destination> destinations, std::string payment_id, bool encrypt_payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er)
{
crypto::hash integrated_payment_id = cryptonote::null_hash;
for (auto it = destinations.begin(); it != destinations.end(); it++)
@@ -144,6 +144,9 @@ namespace tools
}
integrated_payment_id = new_payment_id;
}
+
+ // integrated addresses imply encrypted payment id
+ encrypt_payment_id = true;
}
if (!payment_id.empty())
@@ -161,7 +164,7 @@ namespace tools
}
std::string extra_nonce;
- cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
+ cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id, encrypt_payment_id);
/* Append Payment ID data into extra */
if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) {
@@ -189,7 +192,7 @@ namespace tools
}
// validate the transfer requested and populate dsts & extra
- if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er))
+ if (!validate_transfer(req.destinations, req.payment_id, req.encrypt_payment_id, dsts, extra, er))
{
return false;
}
@@ -247,7 +250,7 @@ namespace tools
}
// validate the transfer requested and populate dsts & extra; RPC_TRANSFER::request and RPC_TRANSFER_SPLIT::request are identical types.
- if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er))
+ if (!validate_transfer(req.destinations, req.payment_id, req.encrypt_payment_id, dsts, extra, er))
{
return false;
}
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 73411a98d..fbe0964d1 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -79,7 +79,7 @@ namespace tools
//json_rpc
bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er);
bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er);
- bool validate_transfer(const std::list<wallet_rpc::transfer_destination> destinations, const std::string payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er);
+ bool validate_transfer(const std::list<wallet_rpc::transfer_destination> destinations, const std::string payment_id, bool encrypt_payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er);
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er);
bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 7786ab009..8897397af 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -97,6 +97,7 @@ namespace wallet_rpc
uint64_t mixin;
uint64_t unlock_time;
std::string payment_id;
+ bool encrypt_payment_id;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
@@ -104,6 +105,7 @@ namespace wallet_rpc
KV_SERIALIZE(mixin)
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(payment_id)
+ KV_SERIALIZE(encrypt_payment_id)
END_KV_SERIALIZE_MAP()
};
@@ -127,6 +129,7 @@ namespace wallet_rpc
uint64_t unlock_time;
std::string payment_id;
bool new_algorithm;
+ bool encrypt_payment_id;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
@@ -135,6 +138,7 @@ namespace wallet_rpc
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(payment_id)
KV_SERIALIZE(new_algorithm)
+ KV_SERIALIZE(encrypt_payment_id)
END_KV_SERIALIZE_MAP()
};