Merge pull request #11206

2fe7534 Add checks in cryptonote serialization routine (Lee *!* Clagett)

ACKs: jpk68, j-berman, selsta, plowsof
This commit is contained in:
tobtoht
2026-09-09 20:07:40 +00:00
4 changed files with 45 additions and 23 deletions
+2 -2
View File
@@ -258,7 +258,7 @@ namespace cryptonote
ar.tag("signatures");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures);
PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures, 0);
bool signatures_not_expected = signatures.empty();
if (!signatures_not_expected && vin.size() != signatures.size())
return false;
@@ -274,7 +274,7 @@ namespace cryptonote
return false;
}
PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]);
PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i], sizeof(crypto::signature));
if (signature_size != signatures[i].size())
return false;
+25 -13
View File
@@ -346,7 +346,7 @@ namespace rct {
{
ar.tag("pseudoOuts");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts);
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts, sizeof(key));
if (pseudoOuts.size() != inputs)
return false;
for (size_t i = 0; i < inputs; ++i)
@@ -360,12 +360,18 @@ namespace rct {
ar.tag("ecdhInfo");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, ecdhInfo);
const bool compressed_ecdh =
(type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus);
const std::size_t min_size = compressed_ecdh ? sizeof(crypto::hash8) : sizeof(key) * 2;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, ecdhInfo, min_size);
if (ecdhInfo.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
{
if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus)
if (compressed_ecdh)
{
// Since RCTTypeBulletproof2 enote types, we don't serialize the blinding factor, and only serialize the
// first 8 bytes of ecdhInfo[i].amount
@@ -391,7 +397,7 @@ namespace rct {
ar.tag("outPk");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, outPk);
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, outPk, sizeof(key));
if (outPk.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
@@ -444,7 +450,7 @@ namespace rct {
ar.begin_array();
if (nbp > outputs)
return false;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs_plus);
PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs_plus, sizeof(key) * 6);
for (size_t i = 0; i < nbp; ++i)
{
FIELDS(bulletproofs_plus[i])
@@ -466,7 +472,7 @@ namespace rct {
ar.begin_array();
if (nbp > outputs)
return false;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs);
PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs, sizeof(key) * 9);
for (size_t i = 0; i < nbp; ++i)
{
FIELDS(bulletproofs[i])
@@ -481,7 +487,7 @@ namespace rct {
{
ar.tag("rangeSigs");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, rangeSigs);
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, rangeSigs, sizeof(key) * 193);
if (rangeSigs.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
@@ -497,7 +503,7 @@ namespace rct {
{
ar.tag("CLSAGs");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, CLSAGs);
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, CLSAGs, sizeof(key) * 3);
if (CLSAGs.size() != inputs)
return false;
for (size_t i = 0; i < inputs; ++i)
@@ -508,7 +514,7 @@ namespace rct {
ar.begin_object();
ar.tag("s");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mixin + 1, CLSAGs[i].s);
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mixin + 1, CLSAGs[i].s, sizeof(key));
if (CLSAGs[i].s.size() != mixin + 1)
return false;
for (size_t j = 0; j <= mixin; ++j)
@@ -540,9 +546,12 @@ namespace rct {
// we keep a byte for size of MGs, because we don't know whether this is
// a simple or full rct signature, and it's starting to annoy the hell out of me
size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2) ? inputs : 1;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs);
// Each MGs has `cc` (`key`) AND at least 1 MGs[i].ss which has at least 1 `key`
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs, sizeof(key) * 2);
if (MGs.size() != mg_elements)
return false;
for (size_t i = 0; i < mg_elements; ++i)
{
// we save the MGs contents directly, because we want it to save its
@@ -551,14 +560,17 @@ namespace rct {
ar.begin_object();
ar.tag("ss");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mixin + 1, MGs[i].ss);
// each MGs[i].ss has at least one `key`
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mixin + 1,MGs[i].ss, sizeof(key));
if (MGs[i].ss.size() != mixin + 1)
return false;
for (size_t j = 0; j < mixin + 1; ++j)
{
ar.begin_array();
size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2) ? 1 : inputs) + 1;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]);
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j], sizeof(key));
if (MGs[i].ss[j].size() != mg_ss2_elements)
return false;
for (size_t k = 0; k < mg_ss2_elements; ++k)
@@ -588,7 +600,7 @@ namespace rct {
{
ar.tag("pseudoOuts");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts);
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts, sizeof(key));
if (pseudoOuts.size() != inputs)
return false;
for (size_t i = 0; i < inputs; ++i)
+1 -1
View File
@@ -46,7 +46,7 @@ bool do_serialize(Archive<false> &ar, std::vector<crypto::signature> &v)
v.clear();
// very basic sanity check
if (ar.remaining_bytes() < cnt*sizeof(crypto::signature)) {
if (ar.remaining_bytes() / sizeof(crypto::signature) < cnt) {
ar.set_fail();
return false;
}
+17 -7
View File
@@ -195,10 +195,15 @@ inline auto do_serialize(Archive &ar, T &v, Args&&... args)
template <bool W, template <bool> class Archive> \
bool do_serialize_object(Archive<W> &ar, stype &v) {
/*! \macro PREPARE_CUSTOM_VECTOR_SERIALIZATION
/*! \macro RESERVE_VECTOR_SERIALIZATION. `min_wire` must be the minimum bytes
* each element takes on the wire.
*/
#define PREPARE_CUSTOM_VECTOR_SERIALIZATION(size, vec) \
::serialization::detail::prepare_custom_vector_serialization(size, vec, typename Archive<W>::is_saving())
#define PREPARE_CUSTOM_VECTOR_SERIALIZATION(size, vec, min_wire) \
do \
{ \
if (!::serialization::detail::prepare_custom_vector_serialization(size, vec, min_wire, ar)) \
return false; \
} while (0);
/*! \macro END_SERIALIZE
* \brief self-explanatory
@@ -295,6 +300,7 @@ inline auto do_serialize(Archive &ar, T &v, Args&&... args)
namespace serialization {
/*! \namespace detail
*
* \brief declaration and default definition for the functions used the API
@@ -306,15 +312,19 @@ namespace serialization {
*
* prepares the vector /vec for serialization
*/
template <typename T>
void prepare_custom_vector_serialization(size_t size, std::vector<T>& vec, const boost::mpl::bool_<true>& /*is_saving*/)
template <typename T, template<bool> class A>
constexpr bool prepare_custom_vector_serialization(size_t, const std::vector<T>& vec, size_t, const A<true>&) noexcept
{
return true;
}
template <typename T>
void prepare_custom_vector_serialization(size_t size, std::vector<T>& vec, const boost::mpl::bool_<false>& /*is_saving*/)
template <typename T, template<bool> class A>
bool prepare_custom_vector_serialization(const size_t size, std::vector<T>& vec, const size_t min_size, const A<false>& ar)
{
if (size && ar.remaining_bytes() / size < min_size)
return false;
vec.resize(size);
return true;
}
/*! \fn do_check_stream_state