aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormoneromooo-monero <moneromooo-monero@users.noreply.github.com>2015-08-03 21:15:10 +0100
committermoneromooo-monero <moneromooo-monero@users.noreply.github.com>2015-08-09 10:13:41 +0100
commite40cfc4e29e046bc57a5995cfd0d46022b7bf285 (patch)
treec0b8c0263326e12fb07583168f1374981c3a98ab
parentMerge pull request #358 (diff)
downloadmonero-e40cfc4e29e046bc57a5995cfd0d46022b7bf285.tar.xz
Encrypted payment IDs
A payment ID may be encrypted using the tx secret key and the receiver's public view key. The receiver can decrypt it with the tx public key and the receiver's secret view key. Using integrated addresses now cause the payment IDs to be encrypted. Payment IDs used manually are not encrypted by default, but can be encrypted using the new 'encrypt_payment_id' field in the transfer and transfer_split RPC calls. It is not possible to use an encrypted payment ID by specifying a manual simplewallet transfer/transfer_new command, though this is just a limitation due to input parsing.
Diffstat (limited to '')
-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()
};