fixed InProofV3 calcs / checks

This commit is contained in:
Some Random Crypto Guy
2025-12-08 16:36:12 +00:00
parent 10b58aac73
commit 305b92909e
9 changed files with 313 additions and 66 deletions
+240 -31
View File
@@ -91,6 +91,16 @@ namespace crypto {
return &reinterpret_cast<const unsigned char &>(scalar);
}
static const mx25519_impl* get_mx25519_impl()
{
static std::once_flag of;
static const mx25519_impl *impl;
std::call_once(of, [&](){ impl = mx25519_select_impl(MX25519_TYPE_AUTO); });
if (impl == nullptr)
throw std::runtime_error("failed to obtain a mx25519 implementation");
return impl;
}
boost::mutex &get_random_lock()
{
static boost::mutex random_lock;
@@ -512,7 +522,194 @@ namespace crypto {
const boost::optional<public_key> &B, // Ed if present
const public_key &D, // X25519 u-coordinate
const secret_key &r,
const secret_key &a,
signature &sig)
{
// Check if we are sender or receiver
if (r != crypto::null_skey) {
// SENDER
generate_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, r, a, sig);
return;
}
// RECEIVER
// Load points (A and B and R) into ge_p3
ge_p3 A_p3;
ge_p3 B_p3;
ge_p3 R_p3;
if (ge_frombytes_vartime(&A_p3, &A) != 0)
throw std::runtime_error("recipient view pubkey is invalid");
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
throw std::runtime_error("recipient spend pubkey is invalid");
if (ge_frombytes_vartime(&R_p3, &R) != 0)
throw std::runtime_error("tx pubkey is invalid");
#if !defined(NDEBUG)
{
// Debug check D == a*R
mx25519_pubkey D_x25519;
mx25519_scmul_key(get_mx25519_impl(),
&D_x25519,
reinterpret_cast<const mx25519_privkey*>(&a),
reinterpret_cast<const mx25519_pubkey*>(&R));
public_key dbg_D;
memcpy(&dbg_D, &D_x25519, sizeof(mx25519_pubkey));
assert(D == dbg_D);
}
#endif
//
// 1. Pick random nonce k
//
crypto::secret_key k;
random_scalar(k);
static const public_key zero = {{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
}};
s_comm_2 buf;
buf.msg = prefix_hash;
buf.D = D; // X25519 u-coord
buf.R = R; // X25519 u-coord
buf.A = A; // Ed25519
buf.B = B ? *B : zero;
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
buf.sep);
//
// 2. Compute X = ConvertPointE(k*G or k*B)
//
ge_p3 kB_or_kG_p3;
if (B)
ge_scalarmult_p3(&kB_or_kG_p3, &k, &B_p3);
else
ge_scalarmult_base(&kB_or_kG_p3, &k);
mx25519_pubkey X_x25519;
ge_p3_to_x25519(X_x25519.data, &kB_or_kG_p3);
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
//
// 3. Compute Y = k*R
//
mx25519_pubkey Y;
mx25519_scmul_key(get_mx25519_impl(),
&Y,
reinterpret_cast<const mx25519_privkey*>(&k),
reinterpret_cast<const mx25519_pubkey*>(&R));
memcpy(&buf.Y, &Y, sizeof(mx25519_pubkey));
// ---------- Extract and lift R ----------
fe u_R;
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
fe v_R_cand;
if (fe_sqrt_mont(v_R_cand, u_R) != 0)
throw std::runtime_error("R not on curve");
fe x1, y1, x2, y2, v_R_neg;
ge_p3 R_ed1, R_ed2;
// +v (principal)
mont_to_ed(x1, y1, u_R, v_R_cand);
ge_from_xy(&R_ed1, x1, y1);
// -v
fe_neg(v_R_neg, v_R_cand);
mont_to_ed(x2, y2, u_R, v_R_neg);
ge_from_xy(&R_ed2, x2, y2);
// Arbitrarily choose R_sign = true (principal v from fe_sqrt_mont)
bool R_sign = true;
ge_p3 R_ed_correct = R_ed1; // +v
// ---------- Extract and lift D (consistent with chosen R_sign) ----------
fe u_D;
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
fe v_D_cand;
if (fe_sqrt_mont(v_D_cand, u_D) != 0)
throw std::runtime_error("D not on curve");
fe x3, y3, x4, y4, v_D_neg;
// Compute D_ed_true = a * R_ed_correct
ge_p3 D_ed_true;
ge_scalarmult_p3(&D_ed_true, &a, &R_ed_correct);
// Normalize to affine for matching
fe inv_z;
fe_invert(inv_z, D_ed_true.Z);
fe xd_true, yd_true;
fe_mul(xd_true, D_ed_true.X, inv_z);
fe_mul(yd_true, D_ed_true.Y, inv_z);
// +v for D
mont_to_ed(x3, y3, u_D, v_D_cand);
bool D_match1 = fe_equal(x3, xd_true) && fe_equal(y3, yd_true); // Affine match (mont_to_ed gives affine x,y)
// -v for D
fe_neg(v_D_neg, v_D_cand);
mont_to_ed(x4, y4, u_D, v_D_neg);
bool D_match2 = fe_equal(x4, xd_true) && fe_equal(y4, yd_true);
bool D_sign = false;
if (D_match1)
D_sign = true;
else if (D_match2)
D_sign = false;
else
throw std::runtime_error("D lift mismatch with computed D_ed_true");
// Pack signs (MSB is set to [1] for outbound, [0] for inbound
sig.sign_mask =
(R_sign ? 0x01 : 0x00) |
(D_sign ? 0x02 : 0x00);
struct {
s_comm_2 buf;
uint8_t sign_mask;
} challenge_hash;
challenge_hash.buf = buf;
challenge_hash.sign_mask = sig.sign_mask;
//
// 7. Compute challenge c = H(prefix_hash || … || sign_mask)
//
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), sig.c);
//
// 8. Compute response z = k - c*a
//
sc_mulsub(&sig.r, &sig.c, &unwrap(a), &k);
memwipe(&k, sizeof(k));
#if !defined(NDEBUG)
bool ok = check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
assert(ok);
#endif
}
void crypto_ops::generate_carrot_tx_proof_as_sender(
const hash &prefix_hash,
const public_key &R, // X25519 u-coordinate
const public_key &A, // Ed25519
const boost::optional<public_key> &B, // Ed if present
const public_key &D, // X25519 u-coordinate
const secret_key &r,
const secret_key &a,
signature &sig)
{
// Load only Ed points (A and B) into ge_p3
ge_p3 A_p3;
@@ -542,15 +739,15 @@ namespace crypto {
memcpy(&dbg_R, &R_x25519, sizeof(mx25519_pubkey));
assert(R == dbg_R);
// Debug check D == ConvertPointE(r*A)
public_key dbg_D;
ge_p3 dbg_D_p3;
ge_scalarmult_p3(&dbg_D_p3, &r, &A_p3);
mx25519_pubkey D_x25519;
ge_p3_to_x25519(D_x25519.data, &dbg_D_p3);
memcpy(&dbg_D, &D_x25519, sizeof(mx25519_pubkey));
assert(D == dbg_D);
}
@@ -677,7 +874,8 @@ namespace crypto {
//
sig.sign_mask =
(R_sign ? 0x01 : 0x00) |
(D_sign ? 0x02 : 0x00);
(D_sign ? 0x02 : 0x00) |
0x80;
struct {
s_comm_2 buf;
@@ -827,9 +1025,10 @@ namespace crypto {
if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0)
return false;
// Extract sign bits
const bool R_sign = (sig.sign_mask & 0x01) != 0;
const bool D_sign = (sig.sign_mask & 0x02) != 0;
// Extract sign bits and direction flag
const bool R_sign = (sig.sign_mask & 0x01) != 0;
const bool D_sign = (sig.sign_mask & 0x02) != 0;
const bool outbound = (sig.sign_mask & 0x80) != 0;
//
// 1. Reconstruct R_ed and D_ed from X25519 u-coords + sign bits
@@ -862,31 +1061,36 @@ namespace crypto {
ge_from_xy(&D_ed, x_D, y_D);
//
// 2. Compute X' = z*G + c*R_ed (or z*B + c*R_ed) in Edwards
// 2. Compute X'
// If inbound proof, X`= z*G + c*A (or z*B + c*A)
// If outbound proof, X`= z*G + c*R_ed (or z*B + c*R_ed)
//
ge_p3 cR_p3;
ge_scalarmult_p3(&cR_p3, &sig.c, &R_ed);
ge_p3 c_p3;
if (outbound)
ge_scalarmult_p3(&c_p3, &sig.c, &R_ed);
else
ge_scalarmult_p3(&c_p3, &sig.c, &A_p3);
ge_p1p1 X_p1p1;
if (B)
{
// Subaddress: X' = c*R_ed + z*B
ge_p3 rB_p3;
ge_scalarmult_p3(&rB_p3, &sig.r, &B_p3);
ge_cached rB_cached;
{
// Subaddress: X' = c*A + z*B
ge_p3 rB_p3;
ge_scalarmult_p3(&rB_p3, &sig.r, &B_p3);
ge_cached rB_cached;
ge_p3_to_cached(&rB_cached, &rB_p3);
ge_add(&X_p1p1, &cR_p3, &rB_cached);
}
ge_add(&X_p1p1, &c_p3, &rB_cached);
}
else
{
// Main address: X' = c*R_ed + z*G
ge_p3 rG_p3;
ge_scalarmult_base(&rG_p3, &sig.r);
ge_cached rG_cached;
ge_p3_to_cached(&rG_cached, &rG_p3);
ge_add(&X_p1p1, &cR_p3, &rG_cached);
}
{
// Main address: X' = c*R_ed + z*G
ge_p3 rG_p3;
ge_scalarmult_base(&rG_p3, &sig.r);
ge_cached rG_cached;
ge_p3_to_cached(&rG_cached, &rG_p3);
ge_add(&X_p1p1, &c_p3, &rG_cached);
}
ge_p3 X_ed_p3;
ge_p1p1_to_p3(&X_ed_p3, &X_p1p1);
@@ -895,20 +1099,25 @@ namespace crypto {
ge_p3_to_x25519(X_x25519.data, &X_ed_p3);
//
// 3. Compute Y' = c*D_ed + z*A in Edwards
// 3. Compute Y'
// If inbound, Y' = c*D_ed + z*R
// If outbound, Y' = c*D_ed + z*A
//
ge_p3 cD_p3;
ge_scalarmult_p3(&cD_p3, &sig.c, &D_ed);
ge_p3 rA_p3;
ge_scalarmult_p3(&rA_p3, &sig.r, &A_p3);
ge_p3 z_p3;
if (outbound)
ge_scalarmult_p3(&z_p3, &sig.r, &A_p3);
else
ge_scalarmult_p3(&z_p3, &sig.r, &R_ed);
ge_cached rA_cached;
ge_p3_to_cached(&rA_cached, &rA_p3);
ge_cached z_cached;
ge_p3_to_cached(&z_cached, &z_p3);
ge_p1p1 Y_p1p1;
ge_add(&Y_p1p1, &cD_p3, &rA_cached);
ge_add(&Y_p1p1, &cD_p3, &z_cached);
ge_p3 Y_ed_p3;
ge_p1p1_to_p3(&Y_ed_p3, &Y_p1p1);
+14 -5
View File
@@ -132,8 +132,10 @@ namespace crypto {
friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
static void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
friend void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
static void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
friend void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
static void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
friend void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
static void generate_carrot_tx_proof_as_sender(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
friend void generate_carrot_tx_proof_as_sender(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
static bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int);
friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int);
static bool check_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
@@ -268,8 +270,11 @@ namespace crypto {
/* Generation of a carrot tx proof; for carrot transactions, D is in X25519 domain (D = r*ConvertPointE(A))
* instead of Ed25519 domain (D = r*A). This version applies ConvertPointE transformation.
*/
inline void generate_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) {
crypto_ops::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, sig);
inline void generate_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, const secret_key &a, signature &sig) {
crypto_ops::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
}
inline void generate_carrot_tx_proof_as_sender(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, const secret_key &a, signature &sig) {
crypto_ops::generate_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, r, a, sig);
}
inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) {
return crypto_ops::check_tx_proof(prefix_hash, R, A, B, D, sig, version);
@@ -279,7 +284,11 @@ namespace crypto {
inline bool check_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
return crypto_ops::check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
}
/*
inline bool check_carrot_tx_proof_as_sender(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
return crypto_ops::check_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, sig);
}
*/
inline void derive_key_image_generator(const public_key &pub, ec_point &ki_gen) {
crypto_ops::derive_key_image_generator(pub, ki_gen);
}
+2 -2
View File
@@ -203,8 +203,8 @@ namespace hw {
crypto::signature &sig) = 0;
virtual void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) = 0;
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) = 0;
virtual bool open_tx(crypto::secret_key &tx_key) = 0;
+2 -2
View File
@@ -284,8 +284,8 @@ namespace hw {
void device_default::generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) {
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, sig);
const crypto::secret_key &a, crypto::signature &sig) {
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
}
bool device_default::open_tx(crypto::secret_key &tx_key) {
+2 -2
View File
@@ -113,8 +113,8 @@ namespace hw {
crypto::signature &sig) override;
void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) override;
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) override;
bool open_tx(crypto::secret_key &tx_key) override;
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
+3 -3
View File
@@ -1434,11 +1434,11 @@ namespace hw {
}
void device_ledger::generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) {
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) {
// to-do: For now, carrot tx proofs are not supported
AUTO_LOCK_CMD();
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, sig);
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
}
bool device_ledger::open_tx(crypto::secret_key &tx_key) {
+2 -2
View File
@@ -254,8 +254,8 @@ namespace hw {
crypto::signature &sig) override;
void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) override;
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) override;
bool open_tx(crypto::secret_key &tx_key) override;
+43 -15
View File
@@ -13222,13 +13222,18 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt
const boost::optional<crypto::public_key> &B,
const crypto::public_key &D,
const crypto::secret_key &r,
const crypto::secret_key &a,
crypto::signature &sig)
{
if (address.m_is_carrot) {
hwdev.generate_carrot_tx_proof(prefix_hash, R, A, is_subaddress ? B : boost::none, D, r, sig);
hwdev.generate_carrot_tx_proof(prefix_hash, R, A, is_subaddress ? B : boost::none, D, r, a, sig);
} else {
crypto::signature sig_cn;
hwdev.generate_tx_proof(prefix_hash, R, A, is_subaddress ? B : boost::none, D, r, sig_cn);
if (r != crypto::null_skey) {
hwdev.generate_tx_proof(prefix_hash, R, A, is_subaddress ? B : boost::none, D, r, sig_cn);
} else {
hwdev.generate_tx_proof(prefix_hash, A, R, is_subaddress ? B : boost::none, D, a, sig_cn);
}
sig.c = sig_cn.c;
sig.r = sig_cn.r;
sig.sign_mask = 0xFF;
@@ -13307,11 +13312,6 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt
// determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound)
const bool is_out = m_account.get_subaddress_map_ref().count(address.m_spend_public_key) == 0;
// determine if tx_key is invalid and should be skipped
const bool skip_txkey = (tx_key == crypto::null_skey &&
address.m_is_carrot &&
tx.vout.size() == additional_tx_keys.size());
const crypto::hash txid = cryptonote::get_transaction_hash(tx);
std::string prefix_data((const char*)&txid, sizeof(crypto::hash));
prefix_data += message;
@@ -13324,6 +13324,11 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt
if (is_out)
{
// determine if tx_key is invalid and should be skipped
const bool skip_txkey = (tx_key == crypto::null_skey &&
address.m_is_carrot &&
tx.vout.size() == additional_tx_keys.size());
const size_t num_sigs = 1 + additional_tx_keys.size();
shared_secret.resize(num_sigs);
sig.resize(num_sigs);
@@ -13341,7 +13346,7 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt
shared_secret[0]);
calculate_tx_public_key_fn(tx_key, tx_pub_key);
generate_proof_fn(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], tx_key, sig[0]);
generate_proof_fn(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], tx_key, crypto::null_skey, sig[0]);
}
for (size_t i = 1; i < num_sigs; ++i)
{
@@ -13361,16 +13366,23 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt
shared_secret[i]);
calculate_tx_public_key_fn(additional_tx_keys[i - 1], tx_pub_key);
generate_proof_fn(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
generate_proof_fn(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[i], additional_tx_keys[i - 1], crypto::null_skey, sig[i]);
}
sig_str = address.m_is_carrot ? std::string("OutProofV3") : std::string("OutProofV2");
}
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<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
// determine if tx_pub_key is invalid and should be skipped
const bool skip_tx_pubkey = (tx_pub_key == crypto::null_pkey &&
address.m_is_carrot &&
tx.vout.size() == additional_tx_pub_keys.size());
if (!skip_tx_pubkey)
THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found");
const size_t num_sigs = 1 + additional_tx_pub_keys.size();
shared_secret.resize(num_sigs);
sig.resize(num_sigs);
@@ -13379,7 +13391,7 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt
crypto::public_key dummy_pkey;
crypto::secret_key dummy_skey;
if (!skip_txkey) {
if (!skip_tx_pubkey) {
calculate_shared_secret_fn(dummy_pkey,
a,
tx_pub_key,
@@ -13387,11 +13399,15 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt
is_out,
shared_secret[0]);
generate_proof_fn(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], a, sig[0]);
generate_proof_fn(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], crypto::null_skey, a, sig[0]);
}
for (size_t i = 1; i < num_sigs; ++i)
{
// Is this an invalid key?
if (skip_tx_pubkey && additional_tx_pub_keys[i - 1] == crypto::null_pkey)
continue;
calculate_shared_secret_fn(dummy_pkey,
a,
additional_tx_pub_keys[i - 1],
@@ -13399,7 +13415,7 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt
is_out,
shared_secret[i]);
generate_proof_fn(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], address.m_spend_public_key, shared_secret[i], a, sig[i]);
generate_proof_fn(prefix_hash, additional_tx_pub_keys[i - 1], address.m_view_public_key, address.m_spend_public_key, shared_secret[i], crypto::null_skey, a, sig[i]);
}
sig_str = address.m_is_carrot ? std::string("InProofV3") : std::string("InProofV2");
}
@@ -13606,7 +13622,19 @@ bool wallet2::check_tx_proof(const cryptonote::transaction &tx, const cryptonote
{
if (version == 3)
{
//to-do
if (tx_pub_key != crypto::null_pkey) {
good_signature[0] = is_subaddress ?
crypto::check_carrot_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], sig[0]) :
crypto::check_carrot_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], sig[0]);
}
for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
{
if (additional_tx_pub_keys[i] != crypto::null_pkey) {
good_signature[i + 1] = is_subaddress ?
crypto::check_carrot_tx_proof(prefix_hash, additional_tx_pub_keys[i], address.m_view_public_key, address.m_spend_public_key, shared_secret[i + 1], sig[i + 1]) :
crypto::check_carrot_tx_proof(prefix_hash, additional_tx_pub_keys[i], address.m_view_public_key, boost::none, shared_secret[i + 1], sig[i + 1]);
}
}
}
else
{
+5 -4
View File
@@ -123,7 +123,7 @@ TEST(carrot_tx_proofs, fuzz_stability)
prefix_hash,
R_pk, A,
use_subaddress ? boost::make_optional(B) : boost::none,
D_pk, r, sig
D_pk, r, a, sig
);
// 8. Verify
@@ -274,7 +274,7 @@ TEST(carrot_tx_proofs, known_values_mutation_rejection_main_address)
// Generate proof with known values
crypto::signature sig;
crypto::generate_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, r, sig);
crypto::generate_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, r, a, sig);
// Verify original proof works
ASSERT_TRUE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, sig));
@@ -347,7 +347,7 @@ TEST(carrot_tx_proofs, known_values_mutation_rejection_subaddress)
// Tx ID: 01f5e1e56df714e3af919ab443b1acc4b1bebffed03198a9aaf3d22449809453
// Tx priv key
crypto::secret_key r;
crypto::secret_key r, a;
const char* r_hex = "4eccc86c26ac250132d141d1b447e1fe25d0d1e4f3f2d7f3aca10a2633b52808";
ASSERT_TRUE(epee::string_tools::hex_to_pod(r_hex, r));
@@ -359,6 +359,7 @@ TEST(carrot_tx_proofs, known_values_mutation_rejection_subaddress)
ASSERT_TRUE(cryptonote::get_account_address_from_str(info, cryptonote::network_type::TESTNET, recipent_address_str));
A = info.address.m_view_public_key;
B = info.address.m_spend_public_key;
a = crypto::null_skey;
// Compute R = rG (subaddress case)
mx25519_pubkey enote_ephemeral_pubkey_out;
@@ -379,7 +380,7 @@ TEST(carrot_tx_proofs, known_values_mutation_rejection_subaddress)
// Generate proof with known values
crypto::signature sig;
crypto::generate_carrot_tx_proof(prefix_hash, R_G, A, B, D, r, sig);
crypto::generate_carrot_tx_proof(prefix_hash, R_G, A, B, D, r, a, sig);
// Verify original proof works
ASSERT_TRUE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, B, D, sig));