wallet: fix inconsistent tx pubkey handling

(cherry picked from commit 0125cf2697604cf0657f087fe7c42981d5e0c3b0)
This commit is contained in:
selsta
2026-10-04 18:50:24 -04:00
committed by such-gitea
parent e86d98e7ca
commit bd0f37cfb3
11 changed files with 176 additions and 98 deletions
+2
View File
@@ -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};
+11
View File
@@ -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;
+2
View File
@@ -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 *);
+17
View File
@@ -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<ec_point &>(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) {
+6
View File
@@ -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) {
+16 -6
View File
@@ -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;
}
//--------------------------------------------------------------------------------
@@ -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
@@ -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<cryptonote::subaddress_index> recognized_change_index;
if (change_addr)
recognized_change_index = sanity_check_change_address(*change_addr, subaddresses, sender_account_keys);
+5 -5
View File
@@ -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);
+2 -4
View File
@@ -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);
+110 -83
View File
@@ -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_field> 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<crypto::public_key> 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_field> 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<crypto::public_key> 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<int> 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<std::pair<uint32_t,
proof.key_image = td.m_key_image;
subaddr_indices.insert(td.m_subaddr_index);
// get tx pub key
const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(td.m_tx, td.m_pk_index);
THROW_WALLET_EXCEPTION_IF(tx_pub_key == crypto::null_pkey, error::wallet_internal_error, "The tx public key isn't found");
// get tx pub key
std::vector<tx_extra_field> 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<crypto::public_key> 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<std::pair<uint32_t,
error::wallet_internal_error, "Failed to derive subaddress public key");
if (m_subaddresses.count(subaddress_spendkey) == 1)
break;
THROW_WALLET_EXCEPTION_IF(additional_tx_pub_keys.empty(), error::wallet_internal_error,
"Normal tx pub key doesn't derive the expected output, while the additional tx pub keys are empty");
THROW_WALLET_EXCEPTION_IF(proof.index_in_tx >= 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_field> 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<crypto::public_key> 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