ringct: make h2d fallible

One theoretically could have crafted a "long amount" (pre-v10) RingCT transaction
with non-0 padding bytes in the decoded amount which fails receiver scanning, but
passes third-party wallet proof checking.
This commit is contained in:
jeffro256
2026-03-24 21:28:57 -05:00
parent 22d35df8e0
commit 2210f85189
7 changed files with 42 additions and 31 deletions
+7 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2016-2024, Monero Research Labs
// Copyright (c) 2016-2026, Monero Research Labs
//
// Author: Shen Noether <shen.noether@gmx.com>
//
@@ -30,7 +30,11 @@
#include <boost/lexical_cast.hpp>
#include "misc_log_ex.h"
extern "C" {
#include "rctCryptoOps.h"
}
#include "rctOps.h"
using namespace crypto;
using namespace std;
@@ -340,7 +344,8 @@ namespace rct {
//generates a random uint long long (for testing)
xmr_amount randXmrAmount(xmr_amount upperlimit) {
return h2d(skGen()) % (upperlimit);
assert(upperlimit > 0);
return crypto::rand<xmr_amount>() % upperlimit;
}
//Scalar multiplications of curve points
+1 -10
View File
@@ -1,5 +1,5 @@
//#define DBG
// Copyright (c) 2016-2024, Monero Research Labs
// Copyright (c) 2016-2026, Monero Research Labs
//
// Author: Shen Noether <shen.noether@gmx.com>
//
@@ -37,15 +37,6 @@
#include <cstddef>
#include <tuple>
#include "crypto/generic-ops.h"
extern "C" {
#include "crypto/random.h"
#include "crypto/keccak.h"
#include "rctCryptoOps.h"
}
#include "crypto/crypto.h"
#include "rctTypes.h"
//Define this flag when debugging to get additional info on the console
+9 -3
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2016-2024, Monero Research Labs
// Copyright (c) 2016-2026, Monero Research Labs
//
// Author: Shen Noether <shen.noether@gmx.com>
//
@@ -1573,7 +1573,10 @@ namespace rct {
if (equalKeys(C, Ctmp) == false) {
CHECK_AND_ASSERT_THROW_MES(false, "warning, amount decoded incorrectly, will be unable to spend");
}
return h2d(amount);
rct::xmr_amount amount_8;
CHECK_AND_ASSERT_THROW_MES(h2d(amount_8, amount),
"long decoded amount contains superfluous data");
return amount_8;
}
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev) {
@@ -1604,7 +1607,10 @@ namespace rct {
if (equalKeys(C, Ctmp) == false) {
CHECK_AND_ASSERT_THROW_MES(false, "warning, amount decoded incorrectly, will be unable to spend");
}
return h2d(amount);
rct::xmr_amount amount_8;
CHECK_AND_ASSERT_THROW_MES(h2d(amount_8, amount),
"long decoded amount contains superfluous data");
return amount_8;
}
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev) {
+11 -7
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2016-2024, Monero Research Labs
// Copyright (c) 2016-2026, Monero Research Labs
//
// Author: Shen Noether <shen.noether@gmx.com>
//
@@ -140,14 +140,18 @@ namespace rct {
//32 byte key to uint long long
// if the key holds a value > 2^64
// then the value in the first 8 bytes is returned
xmr_amount h2d(const key & test) {
xmr_amount vali = 0;
// then false is returned
bool h2d(xmr_amount &amountd, const key & test) {
amountd = 0;
int j = 0;
for (j = 7; j >= 0; j--) {
vali = (xmr_amount)(vali * 256 + (unsigned char)test.bytes[j]);
for (j = 8; j < 32; ++j) {
if (test.bytes[j])
return false;
}
return vali;
for (j = 7; j >= 0; j--) {
amountd = (xmr_amount)(amountd * 256 + (unsigned char)test.bytes[j]);
}
return true;
}
//32 byte key to int[64]
+3 -3
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2016-2024, Monero Research Labs
// Copyright (c) 2016-2026, Monero Research Labs
//
// Author: Shen Noether <shen.noether@gmx.com>
//
@@ -722,8 +722,8 @@ namespace rct {
void d2b(bits amountb, xmr_amount val);
//32 byte key to uint long long
// if the key holds a value > 2^64
// then the value in the first 8 bytes is returned
xmr_amount h2d(const key &test);
// then false is returned
bool h2d(xmr_amount &amountd, const key &test);
//32 byte key to int[64]
void h2b(bits amountb2, const key & test);
//int[64] to 32 byte key
+5 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2017-2024, The Monero Project
// Copyright (c) 2017-2026, The Monero Project
//
// All rights reserved.
//
@@ -438,7 +438,10 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
rct::key C = tx.rct_signatures.outPk[n].mask;
rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
CHECK_AND_ASSERT_MES(rct::equalKeys(C, Ctmp), false, "Failed to decode amount");
amount += rct::h2d(ecdh_info.amount);
rct::xmr_amount this_amount;
CHECK_AND_ASSERT_MES(rct::h2d(this_amount, ecdh_info.amount),
false, "Decoded long amount contains superfluous data");
amount += this_amount;
}
}
CHECK_AND_ASSERT_MES(n_outs == 2, false, "Not exactly 2 outputs were received");
+6 -4
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
//
// All rights reserved.
//
@@ -974,11 +974,13 @@ static const xmr_amount test_amounts[]={0, 1, 2, 3, 4, 5, 10000, 100000000000000
TEST(ringct, d2h)
{
key k, P1;
skpkGen(k, P1);
key k = skGen();
memset(k.bytes + 8, 0, sizeof(k) - 8);
for (auto amount: test_amounts) {
d2h(k, amount);
ASSERT_TRUE(amount == h2d(k));
xmr_amount h2d_amount;
ASSERT_TRUE(h2d(h2d_amount, k));
ASSERT_EQ(h2d_amount, amount);
}
}