From e40cfc4e29e046bc57a5995cfd0d46022b7bf285 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 3 Aug 2015 21:15:10 +0100 Subject: 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. --- src/wallet/wallet2.cpp | 22 ++++++++++++++++++++-- src/wallet/wallet_rpc_server.cpp | 11 +++++++---- src/wallet/wallet_rpc_server.h | 2 +- src/wallet/wallet_rpc_server_commands_defs.h | 4 ++++ 4 files changed, 32 insertions(+), 7 deletions(-) (limited to 'src/wallet') 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 outs; uint64_t tx_money_got_in_outs = 0; + crypto::public_key tx_pub_key = null_pkey; std::vector 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 destinations, std::string payment_id, std::vector& dsts, std::vector& extra, epee::json_rpc::error& er) + bool wallet_rpc_server::validate_transfer(const std::list destinations, std::string payment_id, bool encrypt_payment_id, std::vector& dsts, std::vector& 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 destinations, const std::string payment_id, std::vector& dsts, std::vector& extra, epee::json_rpc::error& er); + bool validate_transfer(const std::list destinations, const std::string payment_id, bool encrypt_payment_id, std::vector& dsts, std::vector& 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() }; -- cgit v1.2.3 From a2d7a5fb49dedd6c7e024701eefc4c0beade1edd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 9 Aug 2015 10:09:39 +0100 Subject: encrypted payment ids are now 64 bit, instead of 256 bit Pros: - smaller on the blockchain - shorter integrated addresses Cons: - less sparseness - less ability to embed actual information The boolean argument to encrypt payment ids is now gone from the RPC calls, since the decision is made based on the length of the payment id passed. --- src/wallet/wallet2.cpp | 49 +++++++++++++++++++---- src/wallet/wallet2.h | 2 + src/wallet/wallet_rpc_server.cpp | 58 +++++++++++++++++----------- src/wallet/wallet_rpc_server.h | 2 +- src/wallet/wallet_rpc_server_commands_defs.h | 4 -- 5 files changed, 81 insertions(+), 34 deletions(-) (limited to 'src/wallet') diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 12db73423..3d3f1e4de 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -237,20 +237,24 @@ 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)) { - bool encrypted; - if(get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id, encrypted) && encrypted) + crypto::hash8 payment_id8 = null_hash8; + if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) { // We got a payment ID to go with this tx - LOG_PRINT_L2("Found encrypted payment ID: " << payment_id); + LOG_PRINT_L2("Found encrypted payment ID: " << payment_id8); if (tx_pub_key != null_pkey) { - if (!decrypt_payment_id(payment_id, tx_pub_key, m_account.get_keys().m_view_secret_key)) + if (!decrypt_payment_id(payment_id8, tx_pub_key, m_account.get_keys().m_view_secret_key)) { - LOG_PRINT_L0("Failed to decrypt payment ID: " << payment_id); + LOG_PRINT_L0("Failed to decrypt payment ID: " << payment_id8); } else { - LOG_PRINT_L2("Decrypted payment ID: " << payment_id); + LOG_PRINT_L2("Decrypted payment ID: " << payment_id8); + // put the 64 bit decrypted payment id in the first 8 bytes + memcpy(payment_id.data, payment_id8.data, 8); + // rest is already 0, but guard against code changes above + memset(payment_id.data + 8, 0, 24); } } else @@ -258,6 +262,10 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ LOG_PRINT_L1("No public key found in tx, unable to decrypt payment id"); } } + else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + { + LOG_PRINT_L2("Found unencrypted payment ID: " << 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; if (0 < received) @@ -783,7 +791,7 @@ bool wallet2::wallet_valid_path_format(const std::string& file_path) return !file_path.empty(); } //---------------------------------------------------------------------------------------------------- -bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) +bool wallet2::parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) { cryptonote::blobdata payment_id_data; if(!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data)) @@ -796,6 +804,33 @@ bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash& return true; } //---------------------------------------------------------------------------------------------------- +bool wallet2::parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id) +{ + cryptonote::blobdata payment_id_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data)) + return false; + + if(sizeof(crypto::hash8) != payment_id_data.size()) + return false; + + payment_id = *reinterpret_cast(payment_id_data.data()); + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) +{ + if (parse_long_payment_id(payment_id_str, payment_id)) + return true; + crypto::hash8 payment_id8; + if (parse_short_payment_id(payment_id_str, payment_id8)) + { + memcpy(payment_id.data, payment_id8.data, 8); + memset(payment_id.data + 8, 0, 24); + return true; + } + return false; +} +//---------------------------------------------------------------------------------------------------- bool wallet2::prepare_file_names(const std::string& file_path) { do_prepare_file_names(file_path, m_keys_file, m_wallet_file); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 93119cd0b..eee6aa58c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -266,6 +266,8 @@ namespace tools */ static bool wallet_valid_path_format(const std::string& file_path); + static bool parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); + static bool parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id); static bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); static std::vector addresses_from_url(const std::string& url, bool& dnssec_valid); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 4797f76a2..7dfd64eef 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -117,14 +117,15 @@ namespace tools } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::validate_transfer(const std::list destinations, std::string payment_id, bool encrypt_payment_id, std::vector& dsts, std::vector& extra, epee::json_rpc::error& er) + bool wallet_rpc_server::validate_transfer(const std::list destinations, std::string payment_id, std::vector& dsts, std::vector& extra, epee::json_rpc::error& er) { - crypto::hash integrated_payment_id = cryptonote::null_hash; + crypto::hash8 integrated_payment_id = cryptonote::null_hash8; + std::string extra_nonce; for (auto it = destinations.begin(); it != destinations.end(); it++) { cryptonote::tx_destination_entry de; bool has_payment_id; - crypto::hash new_payment_id; + crypto::hash8 new_payment_id; if(!get_account_integrated_address_from_str(de.addr, has_payment_id, new_payment_id, m_wallet.testnet(), it->address)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; @@ -136,17 +137,15 @@ namespace tools if (has_payment_id) { - if (!payment_id.empty() || integrated_payment_id != cryptonote::null_hash) + if (!payment_id.empty() || integrated_payment_id != cryptonote::null_hash8) { er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; er.message = "A single payment id is allowed per transaction"; return false; } integrated_payment_id = new_payment_id; + cryptonote::set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, integrated_payment_id); } - - // integrated addresses imply encrypted payment id - encrypt_payment_id = true; } if (!payment_id.empty()) @@ -155,17 +154,23 @@ namespace tools /* Just to clarify */ const std::string& payment_id_str = payment_id; - crypto::hash payment_id; + crypto::hash long_payment_id; + crypto::hash8 short_payment_id; + /* Parse payment ID */ - if (!wallet2::parse_payment_id(payment_id_str, payment_id)) { + if (wallet2::parse_long_payment_id(payment_id_str, long_payment_id)) { + cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, long_payment_id); + } + /* or short payment ID */ + else if (!wallet2::parse_short_payment_id(payment_id_str, short_payment_id)) { + cryptonote::set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, short_payment_id); + } + else { er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Payment id has invalid format: \"" + payment_id_str + "\", expected 64-character string"; + er.message = "Payment id has invalid format: \"" + payment_id_str + "\", expected 16 or 64 character string"; return false; } - std::string extra_nonce; - 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)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; @@ -192,7 +197,7 @@ namespace tools } // validate the transfer requested and populate dsts & extra - if (!validate_transfer(req.destinations, req.payment_id, req.encrypt_payment_id, dsts, extra, er)) + if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er)) { return false; } @@ -250,7 +255,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, req.encrypt_payment_id, dsts, extra, er)) + if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er)) { return false; } @@ -342,14 +347,14 @@ namespace tools { try { - crypto::hash payment_id; + crypto::hash8 payment_id; if (req.payment_id.empty()) { - crypto::generate_random_bytes(32, payment_id.data); + crypto::generate_random_bytes(8, payment_id.data); } else { - if (!tools::wallet2::parse_payment_id(req.payment_id,payment_id)) + if (!tools::wallet2::parse_short_payment_id(req.payment_id,payment_id)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; er.message = "Invalid payment ID"; @@ -374,7 +379,7 @@ namespace tools try { cryptonote::account_public_address address; - crypto::hash payment_id; + crypto::hash8 payment_id; bool has_payment_id; if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet.testnet(), req.integrated_address)) @@ -488,6 +493,7 @@ namespace tools for (auto & payment_id_str : req.payment_ids) { crypto::hash payment_id; + crypto::hash8 payment_id8; cryptonote::blobdata payment_id_blob; // TODO - should the whole thing fail because of one bad id? @@ -499,15 +505,23 @@ namespace tools return false; } - if(sizeof(payment_id) != payment_id_blob.size()) + if(sizeof(payment_id) == payment_id_blob.size()) + { + payment_id = *reinterpret_cast(payment_id_blob.data()); + } + else if(sizeof(payment_id8) == payment_id_blob.size()) + { + payment_id8 = *reinterpret_cast(payment_id_blob.data()); + memcpy(payment_id.data, payment_id8.data, 8); + memset(payment_id.data + 8, 0, 24); + } + else { er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; er.message = "Payment ID has invalid size: " + payment_id_str; return false; } - payment_id = *reinterpret_cast(payment_id_blob.data()); - std::list payment_list; m_wallet.get_payments(payment_id, payment_list, req.min_block_height); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index fbe0964d1..73411a98d 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 destinations, const std::string payment_id, bool encrypt_payment_id, std::vector& dsts, std::vector& extra, epee::json_rpc::error& er); + bool validate_transfer(const std::list destinations, const std::string payment_id, std::vector& dsts, std::vector& 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 8897397af..7786ab009 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -97,7 +97,6 @@ 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) @@ -105,7 +104,6 @@ namespace wallet_rpc KV_SERIALIZE(mixin) KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) - KV_SERIALIZE(encrypt_payment_id) END_KV_SERIALIZE_MAP() }; @@ -129,7 +127,6 @@ 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) @@ -138,7 +135,6 @@ namespace wallet_rpc KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) KV_SERIALIZE(new_algorithm) - KV_SERIALIZE(encrypt_payment_id) END_KV_SERIALIZE_MAP() }; -- cgit v1.2.3