diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 2c3c14c25..31df4f63a 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -363,7 +363,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; @@ -379,7 +379,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; diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 94fe48aa4..c9836b8dd 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -350,7 +350,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) @@ -364,12 +364,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 || type == RCTTypeBulletproofPlus_FullCommit); + 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 || type == RCTTypeBulletproofPlus_FullCommit) + 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 @@ -395,7 +401,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,8 @@ namespace rct { { ar.tag("bp"); ar.begin_array(); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs); + // Wownero legacy per-output bulletproofs: same minimum wire size as a Bulletproof below. + PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs, sizeof(key) * 9); if (bulletproofs.size() != outputs) return false; for (size_t i = 0; i < outputs; ++i) @@ -463,7 +470,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]) @@ -485,7 +492,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]) @@ -500,7 +507,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) @@ -516,7 +523,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) @@ -527,7 +534,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) @@ -559,9 +566,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 || type == RCTTypeSimpleBulletproof) ? 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 @@ -570,14 +580,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 || type == RCTTypeSimpleBulletproof) ? 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) @@ -607,7 +620,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) diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index c2a4846ee..858752b8b 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -46,7 +46,7 @@ bool do_serialize(Archive &ar, std::vector &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; } diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index 8aae7b492..41df0f1c3 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -139,10 +139,15 @@ inline bool do_serialize(Archive &ar, bool &v) template class Archive> \ bool do_serialize_object(Archive &ar){ -/*! \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::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 @@ -237,6 +242,7 @@ inline bool do_serialize(Archive &ar, bool &v) } while(0); namespace serialization { + /*! \namespace detail * * \brief declaration and default definition for the functions used the API @@ -248,15 +254,19 @@ namespace serialization { * * prepares the vector /vec for serialization */ - template - void prepare_custom_vector_serialization(size_t size, std::vector& vec, const boost::mpl::bool_& /*is_saving*/) + template class A> + constexpr bool prepare_custom_vector_serialization(size_t, const std::vector& vec, size_t, const A&) noexcept { + return true; } - template - void prepare_custom_vector_serialization(size_t size, std::vector& vec, const boost::mpl::bool_& /*is_saving*/) + template class A> + bool prepare_custom_vector_serialization(const size_t size, std::vector& vec, const size_t min_size, const A& ar) { + if (size && ar.remaining_bytes() / size < min_size) + return false; vec.resize(size); + return true; } /*! \fn do_check_stream_state