diff --git a/src/crypto/crypto-ops-data.c b/src/crypto/crypto-ops-data.c index 1a85de60d..bbdd021c7 100644 --- a/src/crypto/crypto-ops-data.c +++ b/src/crypto/crypto-ops-data.c @@ -877,3 +877,5 @@ const ge_p3 ge_p3_H = { {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {23443568, -5110398, -8776029, -4345135, 6889568, -14710814, 7474843, 3279062, 14550766, -7453428} }; + +const unsigned char sc_inv_eight[32] = {0x79, 0x2f, 0xdc, 0xe2, 0x29, 0xe5, 0x06, 0x61, 0xd0, 0xda, 0x1c, 0x7d, 0xb3, 0x9d, 0xd3, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06}; diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index 4b392d472..8694c80ad 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -2307,6 +2307,17 @@ void ge_mul8(ge_p1p1 *r, const ge_p2 *t) { ge_p2_dbl(r, &u); } +void ge_clear_torsion_vartime(unsigned char *out, const ge_p3 *point) { + // mul by inv 8, then mul by 8 + ge_p2 point_inv_8; + ge_scalarmult(&point_inv_8, sc_inv_eight, point); + ge_p1p1 point_inv_8_mul_8; + ge_mul8(&point_inv_8_mul_8, &point_inv_8); + ge_p3 torsion_cleared_point; + ge_p1p1_to_p3(&torsion_cleared_point, &point_inv_8_mul_8); + ge_p3_tobytes(out, &torsion_cleared_point); +} + void ge_fromfe_frombytes_vartime(ge_p2 *r, const unsigned char *s) { fe u, v, w, x, y, z; unsigned char sign; diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index e4901e080..07c529a05 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -135,6 +135,7 @@ void ge_triple_scalarmult_precomp_vartime(ge_p2 *, const unsigned char *, const void ge_double_scalarmult_precomp_vartime2(ge_p2 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp); void ge_double_scalarmult_precomp_vartime2_p3(ge_p3 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp); void ge_mul8(ge_p1p1 *, const ge_p2 *); +void ge_clear_torsion_vartime(unsigned char *, const ge_p3 *); extern const fe fe_ma2; extern const fe fe_ma; extern const fe fe_fffb1; @@ -143,6 +144,7 @@ extern const fe fe_fffb3; extern const fe fe_fffb4; extern const ge_p3 ge_p3_identity; extern const ge_p3 ge_p3_H; +extern const unsigned char sc_inv_eight[32]; void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char *); void sc_0(unsigned char *); void sc_reduce32(unsigned char *); diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 77a36069a..3db7f8d43 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -177,6 +177,23 @@ namespace crypto { return ge_frombytes_vartime(&point, &key) == 0; } + bool get_valid_torsion_cleared_point_vartime(const ec_point &point, ec_point &torsion_cleared_out) { + ge_p3 p3; + if (ge_frombytes_vartime(&p3, &point) != 0) + return false; + ge_clear_torsion_vartime(&torsion_cleared_out, &p3); + if (memcmp(&torsion_cleared_out, &EC_I, sizeof(EC_I)) == 0) + return false; + return true; + } + + public_key pubkey_clear_torsion(const public_key &pubkey) { + public_key result; + if (!get_valid_torsion_cleared_point_vartime(pubkey, result)) + static_cast(result) = EC_I; + return result; + } + bool crypto_ops::secret_key_to_public_key(const secret_key &sec, public_key &pub) { ge_p3 point; if (sc_check(&unwrap(sec)) != 0) { diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index ee1cac04a..9121d2bc5 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -113,6 +113,8 @@ namespace crypto { sizeof(key_derivation) == 32 && sizeof(key_image) == 32 && sizeof(signature) == 64 && sizeof(view_tag) == 1, "Invalid structure size"); + static const ec_point EC_I = {1}; + class crypto_ops { crypto_ops(); crypto_ops(const crypto_ops &); @@ -217,6 +219,10 @@ namespace crypto { return crypto_ops::check_key(key); } + bool get_valid_torsion_cleared_point_vartime(const ec_point &point, ec_point &torsion_cleared_out); + // Return a canonical key in the prime-order subgroup, or identity on decode failure. + public_key pubkey_clear_torsion(const public_key &pubkey); + /* Checks a private key and computes the corresponding public key. */ inline bool secret_key_to_public_key(const secret_key &sec, public_key &pub) { diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index caafddc86..c9962a0ef 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -39,6 +39,7 @@ using namespace epee; #include "misc_language.h" #include "common/base58.h" #include "crypto/hash.h" +#include "ringct/rctOps.h" #include "int-util.h" #include "common/dns_utils.h" @@ -172,6 +173,15 @@ namespace cryptonote { return tools::base58::encode_addr(integrated_address_prefix, t_serializable_object_to_blob(iadr)); } //----------------------------------------------------------------------- + bool check_address(const account_public_address& adr) + { + const auto valid_key = [](const crypto::public_key& key) { + const rct::key point = rct::pk2rct(key); + return !(point == rct::identity()) && rct::isInMainSubgroup(point); + }; + return valid_key(adr.m_spend_public_key) && valid_key(adr.m_view_public_key); + } + bool get_account_address_from_str( address_parse_info& info , network_type nettype @@ -233,12 +243,6 @@ namespace cryptonote { return false; } } - - if (!crypto::check_key(info.address.m_spend_public_key) || !crypto::check_key(info.address.m_view_public_key)) - { - LOG_PRINT_L1("Failed to validate address keys"); - return false; - } } else { @@ -274,6 +278,12 @@ namespace cryptonote { info.has_payment_id = false; } + if (!check_address(info.address)) + { + LOG_PRINT_L1("Failed to validate address keys"); + return false; + } + return true; } //-------------------------------------------------------------------------------- diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index 57b980aab..70c04d862 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -95,6 +95,8 @@ namespace cryptonote { , const crypto::hash8& payment_id ); + bool check_address(const account_public_address& adr); + bool get_account_address_from_str( address_parse_info& info , network_type nettype diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 6f2ac8813..0167e010c 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -250,6 +250,9 @@ namespace cryptonote return false; } + for (const tx_destination_entry& destination : destinations) + CHECK_AND_ASSERT_MES(check_address(destination.addr), false, "Invalid destination address keys"); + boost::optional recognized_change_index; if (change_addr) recognized_change_index = sanity_check_change_address(*change_addr, subaddresses, sender_account_keys); diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp index 0e59a16ba..cfa1a76d4 100644 --- a/src/device_trezor/trezor/protocol.cpp +++ b/src/device_trezor/trezor/protocol.cpp @@ -155,12 +155,12 @@ namespace ki { res.emplace_back(); auto & cres = res.back(); cres.set_out_key(key_to_string(td.get_public_key())); - cres.set_tx_pub_key(key_to_string(tx_pub_key)); + cres.set_tx_pub_key(key_to_string(::crypto::pubkey_clear_torsion(tx_pub_key))); cres.set_internal_output_index(td.m_internal_output_index); cres.set_sub_addr_major(td.m_subaddr_index.major); cres.set_sub_addr_minor(td.m_subaddr_index.minor); if (!additional_tx_pub_keys.empty() && additional_tx_pub_keys.size() > td.m_internal_output_index) { - cres.add_additional_tx_pub_keys(key_to_string(additional_tx_pub_keys[td.m_internal_output_index])); + cres.add_additional_tx_pub_keys(key_to_string(::crypto::pubkey_clear_torsion(additional_tx_pub_keys[td.m_internal_output_index]))); } } @@ -467,10 +467,10 @@ namespace tx { } } - dst->set_real_out_tx_key(key_to_string(src.real_out_tx_key)); + dst->set_real_out_tx_key(key_to_string(::crypto::pubkey_clear_torsion(src.real_out_tx_key))); dst->set_real_output_in_tx_index(src.real_output_in_tx_index); - if (!src.real_out_additional_tx_keys.empty()) { - dst->add_real_out_additional_tx_keys(key_to_string(src.real_out_additional_tx_keys.at(src.real_output_in_tx_index))); + if (src.real_output_in_tx_index < src.real_out_additional_tx_keys.size()) { + dst->add_real_out_additional_tx_keys(key_to_string(::crypto::pubkey_clear_torsion(src.real_out_additional_tx_keys.at(src.real_output_in_tx_index)))); } dst->set_amount(src.amount); dst->set_rct(src.rct); diff --git a/src/wallet/pending_tx_validation.cpp b/src/wallet/pending_tx_validation.cpp index af2cbf584..c5dacf38b 100644 --- a/src/wallet/pending_tx_validation.cpp +++ b/src/wallet/pending_tx_validation.cpp @@ -810,10 +810,8 @@ void sanity_check_pending_tx(const wallet2::pending_tx &ptx, // Check that addresses keys are canonical. // Without this check, the following duplicate address check may be weakened. - CHECK_AND_ASSERT_THROW_MES(rct::isInMainSubgroup(rct::pk2rct(dest.addr.m_spend_public_key)), - "sanity_check_pending_tx: destination spendkey is not in the main subgroup (suspicious!)"); - CHECK_AND_ASSERT_THROW_MES(rct::isInMainSubgroup(rct::pk2rct(dest.addr.m_view_public_key)), - "sanity_check_pending_tx: destination viewkey is not in the main subgroup (suspicious!)"); + CHECK_AND_ASSERT_THROW_MES(cryptonote::check_address(dest.addr), + "sanity_check_pending_tx: invalid destination address keys"); // Check that duplicate addresses are all either subaddresses or normal addresses (ignoring is_integrated). const auto &dup = dest_duplicates.find(dest.addr.m_spend_public_key); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 3fb73b08f..45a78eebe 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2892,24 +2892,17 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote // We got a payment ID to go with this tx LOG_PRINT_L2("Found encrypted payment ID: " << payment_id8); MINFO("Consider using subaddresses instead of encrypted payment IDs"); - if (tx_pub_key != null_pkey) + if (!m_account.get_device().decrypt_payment_id(payment_id8, crypto::pubkey_clear_torsion(tx_pub_key), m_account.get_keys().m_view_secret_key)) { - if (!m_account.get_device().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_id8); - } - else - { - 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); - } + LOG_PRINT_L0("Failed to decrypt payment ID: " << payment_id8); } else { - LOG_PRINT_L1("No public key found in tx, unable to decrypt 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 if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) @@ -13123,7 +13116,7 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon"); - THROW_WALLET_EXCEPTION_IF(!additional_derivations.empty() && additional_derivations.size() != tx.vout.size(), error::wallet_internal_error, + THROW_WALLET_EXCEPTION_IF(additional_derivations.size() > tx.vout.size(), error::wallet_internal_error, "The size of additional derivations is wrong"); check_tx_key_helper(tx, derivation, additional_derivations, address, received); @@ -13151,8 +13144,14 @@ bool wallet2::is_out_to_acc(const cryptonote::account_public_address &address, c crypto::public_key derived_out_key; bool found = false; bool r; + + const auto is_null_derivation = [](const crypto::key_derivation &d) { + static const crypto::key_derivation null_derivation{}; + return memcmp(&d, &null_derivation, sizeof(d)) == 0; + }; + // first run quick check if output has matching view tag, otherwise output should not belong to account - if (out_can_be_to_acc(view_tag_opt, derivation, output_index)) + if (!is_null_derivation(derivation) && out_can_be_to_acc(view_tag_opt, derivation, output_index)) { // if view tag match, run slower check deriving output pub key and comparing to expected r = crypto::derive_public_key(derivation, output_index, address.m_spend_public_key, derived_out_key); @@ -13164,12 +13163,10 @@ bool wallet2::is_out_to_acc(const cryptonote::account_public_address &address, c } } - if (!found && !additional_derivations.empty()) + if (!found && output_index < additional_derivations.size()) { - THROW_WALLET_EXCEPTION_IF(output_index >= additional_derivations.size(), error::wallet_internal_error, - "wrong number of additional derivations"); const crypto::key_derivation &additional_derivation = additional_derivations[output_index]; - if (out_can_be_to_acc(view_tag_opt, additional_derivation, output_index)) + if (!is_null_derivation(additional_derivation) && out_can_be_to_acc(view_tag_opt, additional_derivation, output_index)) { r = crypto::derive_public_key(additional_derivation, output_index, address.m_spend_public_key, derived_out_key); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key"); @@ -13279,10 +13276,16 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt } else { - crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); - THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found"); + std::vector tx_extra_fields; + parse_tx_extra(tx.extra, tx_extra_fields); + tx_extra_pub_key pub_key_field; + THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field), + error::wallet_internal_error, "Tx pubkey was not found"); + crypto::public_key tx_pub_key = crypto::pubkey_clear_torsion(pub_key_field.pub_key); std::vector additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx); + for (crypto::public_key &key : additional_tx_pub_keys) + key = crypto::pubkey_clear_torsion(key); const size_t num_sigs = 1 + additional_tx_pub_keys.size(); shared_secret.resize(num_sigs); sig.resize(num_sigs); @@ -13290,6 +13293,33 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt const crypto::secret_key& a = m_account.get_keys().m_view_secret_key; hwdev.scalarmultKey(aP, rct::pk2rct(tx_pub_key), rct::sk2rct(a)); shared_secret[0] = rct2pk(aP); + + const auto has_received = [&](const crypto::public_key &candidate_shared_secret) { + crypto::key_derivation derivation; + THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(candidate_shared_secret, rct::rct2sk(rct::I), derivation), + error::wallet_internal_error, "Failed to generate key derivation"); + uint64_t received{0}; + check_tx_key_helper(tx, derivation, {}, address, received); + return received > 0; + }; + + size_t pk_index = 1; + if (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index) && !has_received(shared_secret[0])) + { + do + { + const crypto::public_key candidate = crypto::pubkey_clear_torsion(pub_key_field.pub_key); + hwdev.scalarmultKey(aP, rct::pk2rct(candidate), rct::sk2rct(a)); + const crypto::public_key candidate_shared_secret = rct::rct2pk(aP); + if (has_received(candidate_shared_secret)) + { + tx_pub_key = candidate; + shared_secret[0] = candidate_shared_secret; + break; + } + } while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, ++pk_index)); + } + if (is_subaddress) { hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], a, sig[0]); @@ -13415,10 +13445,16 @@ bool wallet2::check_tx_proof(const cryptonote::transaction &tx, const cryptonote memcpy(&sig[i], sig_decoded.data(), sizeof(crypto::signature)); } - crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); - THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found"); + std::vector tx_extra_fields; + parse_tx_extra(tx.extra, tx_extra_fields); + tx_extra_pub_key pub_key_field; + THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field), + error::wallet_internal_error, "Tx pubkey was not found"); std::vector additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx); + if (!is_out) + for (crypto::public_key &key : additional_tx_pub_keys) + key = crypto::pubkey_clear_torsion(key); THROW_WALLET_EXCEPTION_IF(additional_tx_pub_keys.size() + 1 != num_sigs, error::wallet_internal_error, "Signature size mismatch with additional tx pubkeys"); const crypto::hash txid = cryptonote::get_transaction_hash(tx); @@ -13429,12 +13465,31 @@ bool wallet2::check_tx_proof(const cryptonote::transaction &tx, const cryptonote // check signature std::vector good_signature(num_sigs, 0); + size_t pk_index = 0; + bool has_tx_pub_key = false; + while (!good_signature[0] && find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) + { + const crypto::public_key tx_pub_key = is_out ? pub_key_field.pub_key : crypto::pubkey_clear_torsion(pub_key_field.pub_key); + if (is_out && tx_pub_key == null_pkey) + continue; + has_tx_pub_key = true; + if (is_out) + { + good_signature[0] = is_subaddress ? + crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], sig[0], version) : + crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], sig[0], version); + } + else + { + good_signature[0] = is_subaddress ? + crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], sig[0], version) : + crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], sig[0], version); + } + } + THROW_WALLET_EXCEPTION_IF(!has_tx_pub_key, error::wallet_internal_error, "Tx pubkey was not found"); + if (is_out) { - good_signature[0] = is_subaddress ? - crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], sig[0], version) : - crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], sig[0], version); - for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i) { good_signature[i + 1] = is_subaddress ? @@ -13444,10 +13499,6 @@ bool wallet2::check_tx_proof(const cryptonote::transaction &tx, const cryptonote } else { - good_signature[0] = is_subaddress ? - crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], sig[0], version) : - crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], sig[0], version); - for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i) { good_signature[i + 1] = is_subaddress ? @@ -13459,7 +13510,7 @@ bool wallet2::check_tx_proof(const cryptonote::transaction &tx, const cryptonote if (std::any_of(good_signature.begin(), good_signature.end(), [](int i) { return i > 0; })) { // obtain key derivation by multiplying scalar 1 to the shared secret - crypto::key_derivation derivation; + crypto::key_derivation derivation{}; if (good_signature[0]) THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[0], rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation"); @@ -13530,16 +13581,20 @@ std::string wallet2::get_reserve_proof(const boost::optional tx_extra_fields; + parse_tx_extra(td.m_tx.extra, tx_extra_fields); + tx_extra_pub_key pub_key_field; + THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, td.m_pk_index), + error::wallet_internal_error, "The tx public key isn't found"); + const crypto::public_key tx_pub_key = pub_key_field.pub_key; const std::vector additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); // determine which tx pub key was used for deriving the output key - const crypto::public_key *tx_pub_key_used = &tx_pub_key; + crypto::public_key tx_pub_key_used = crypto::pubkey_clear_torsion(tx_pub_key); for (int i = 0; i < 2; ++i) { - proof.shared_secret = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(*tx_pub_key_used), rct::sk2rct(m_account.get_keys().m_view_secret_key))); + proof.shared_secret = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(tx_pub_key_used), rct::sk2rct(m_account.get_keys().m_view_secret_key))); crypto::key_derivation derivation; THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(proof.shared_secret, rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation"); @@ -13548,15 +13603,15 @@ std::string wallet2::get_reserve_proof(const boost::optional= additional_tx_pub_keys.size(), error::wallet_internal_error, + "Normal tx pub key doesn't derive the expected output, and no additional tx pub key exists for this output index"); THROW_WALLET_EXCEPTION_IF(i == 1, error::wallet_internal_error, "Neither normal tx pub key nor additional tx pub key derive the expected output key"); - tx_pub_key_used = &additional_tx_pub_keys[proof.index_in_tx]; + tx_pub_key_used = crypto::pubkey_clear_torsion(additional_tx_pub_keys[proof.index_in_tx]); } // generate signature for shared secret - crypto::generate_tx_proof(prefix_hash, m_account.get_keys().m_account_address.m_view_public_key, *tx_pub_key_used, boost::none, proof.shared_secret, m_account.get_keys().m_view_secret_key, proof.shared_secret_sig); + crypto::generate_tx_proof(prefix_hash, m_account.get_keys().m_account_address.m_view_public_key, tx_pub_key_used, boost::none, proof.shared_secret, m_account.get_keys().m_view_secret_key, proof.shared_secret_sig); // derive ephemeral secret key crypto::key_image ki; @@ -13717,14 +13772,19 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr THROW_WALLET_EXCEPTION_IF(!get_output_public_key(tx.vout[proof.index_in_tx], output_public_key), error::wallet_internal_error, "Output key wasn't found"); // get tx pub key - const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); - THROW_WALLET_EXCEPTION_IF(tx_pub_key == crypto::null_pkey, error::wallet_internal_error, "The tx public key isn't found"); + std::vector tx_extra_fields; + parse_tx_extra(tx.extra, tx_extra_fields); + tx_extra_pub_key tx_pub_key; + THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, tx_pub_key), error::wallet_internal_error, "The tx public key isn't found"); const std::vector additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx); // check singature for shared secret - ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, proof.shared_secret, proof.shared_secret_sig, version); - if (!ok && additional_tx_pub_keys.size() == tx.vout.size()) - ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[proof.index_in_tx], boost::none, proof.shared_secret, proof.shared_secret_sig, version); + ok = false; + size_t tx_pub_key_index = 0; + while (!ok && find_tx_extra_field_by_type(tx_extra_fields, tx_pub_key, tx_pub_key_index++)) + ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, crypto::pubkey_clear_torsion(tx_pub_key.pub_key), boost::none, proof.shared_secret, proof.shared_secret_sig, version); + if (!ok && proof.index_in_tx < additional_tx_pub_keys.size()) + ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, crypto::pubkey_clear_torsion(additional_tx_pub_keys[proof.index_in_tx]), boost::none, proof.shared_secret, proof.shared_secret_sig, version); if (!ok) return false; @@ -14094,44 +14154,11 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key } - // Due to a previous bug, there might be more than one tx pubkey in extra, one being - // the result of a previously discarded signature. - // For speed, since scanning for outputs is a slow process, we check whether extra - // contains more than one pubkey. If not, the first one is returned. If yes, they're - // checked for whether they yield at least one output + // Use the tx pubkey index recorded for this output. tx_extra_pub_key pub_key_field; - THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 0), error::wallet_internal_error, + THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, td.m_pk_index), error::wallet_internal_error, "Public key wasn't found in the transaction extra"); - const crypto::public_key tx_pub_key = pub_key_field.pub_key; - bool two_found = find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 1); - if (!two_found) { - // easy case, just one found - return tx_pub_key; - } - - // more than one, loop and search - const cryptonote::account_keys& keys = m_account.get_keys(); - size_t pk_index = 0; - hw::device &hwdev = m_account.get_device(); - - while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) { - const crypto::public_key tx_pub_key = pub_key_field.pub_key; - crypto::key_derivation derivation; - bool r = hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation); - THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation"); - - for (size_t i = 0; i < td.m_tx.vout.size(); ++i) - { - tx_scan_info_t tx_scan_info; - check_acc_out_precomp(td.m_tx.vout[i], derivation, {}, i, tx_scan_info); - if (!tx_scan_info.error && tx_scan_info.received) - return tx_pub_key; - } - } - - // we found no key yielding an output, but it might be in the additional - // tx pub keys only, which we do not need to check, so return the first one - return tx_pub_key; + return pub_key_field.pub_key; } bool wallet2::export_key_images(const std::string &filename, bool all) const