diff options
| author | Monzero Build System <builds@monzero.org> | 2026-08-15 21:55:20 +0100 |
|---|---|---|
| committer | Monzero Build System <builds@monzero.org> | 2026-08-15 21:55:20 +0100 |
| commit | e933bd36cee44b42d631c1c64e9f3df027be7ab0 (patch) | |
| tree | 0a35af5b088d2226a693cfee82dd12b5a813b31f /src/cryptonote_basic | |
| parent | fb59a510e4c61e9b971c5197856144fda7b6ec7a (diff) | |
| download | monzero-core-e933bd36cee44b42d631c1c64e9f3df027be7ab0.tar.gz monzero-core-e933bd36cee44b42d631c1c64e9f3df027be7ab0.tar.xz monzero-core-e933bd36cee44b42d631c1c64e9f3df027be7ab0.zip | |
Add strict canonical asset descriptor decoding
Diffstat (limited to 'src/cryptonote_basic')
| -rw-r--r-- | src/cryptonote_basic/asset_types.cpp | 89 | ||||
| -rw-r--r-- | src/cryptonote_basic/asset_types.h | 1 |
2 files changed, 90 insertions, 0 deletions
diff --git a/src/cryptonote_basic/asset_types.cpp b/src/cryptonote_basic/asset_types.cpp index faac669d0..927650b5a 100644 --- a/src/cryptonote_basic/asset_types.cpp +++ b/src/cryptonote_basic/asset_types.cpp @@ -5,6 +5,8 @@ #include <limits> #include <map> +#include <boost/uuid/uuid.hpp> + #include "crypto/hash.h" namespace cryptonote @@ -42,6 +44,36 @@ namespace assets for (unsigned shift = 0; shift < 64; shift += 8) target.push_back(static_cast<uint8_t>(value >> shift)); } + + template<typename T> + bool read_pod(const std::vector<uint8_t>& source, size_t& offset, T& value) + { + if (offset > source.size() || sizeof(value) > source.size() - offset) + return false; + std::memcpy(&value, source.data() + offset, sizeof(value)); + offset += sizeof(value); + return true; + } + + bool read_u16_le(const std::vector<uint8_t>& source, size_t& offset, uint16_t& value) + { + if (offset > source.size() || 2 > source.size() - offset) + return false; + value = static_cast<uint16_t>(source[offset]) + | static_cast<uint16_t>(source[offset + 1]) << 8; + offset += 2; + return true; + } + + bool read_u64_le(const std::vector<uint8_t>& source, size_t& offset, uint64_t& value) + { + if (offset > source.size() || 8 > source.size() - offset) + return false; + value = 0; + for (unsigned shift = 0; shift < 64; shift += 8) + value |= static_cast<uint64_t>(source[offset++]) << shift; + return true; + } } bool validate_issuance_descriptor(const issuance_descriptor& descriptor, std::string* error) @@ -102,6 +134,63 @@ namespace assets return true; } + bool decode_issuance_descriptor(const std::vector<uint8_t>& encoded, issuance_descriptor& descriptor, std::string* error) + { + constexpr size_t domain_size = sizeof(DOMAIN) - 1; + constexpr size_t fixed_size = domain_size + 1 + 16 + 1 + 32 + 32 + 8 + 1 + 32 + 32 + 2; + if (encoded.size() < fixed_size) + return fail(error, "truncated issuance descriptor"); + if (!std::equal(DOMAIN, DOMAIN + domain_size, encoded.begin())) + return fail(error, "invalid issuance descriptor domain"); + + issuance_descriptor parsed; + size_t offset = domain_size; + parsed.version = encoded[offset++]; + + boost::uuids::uuid network_id{}; + if (!read_pod(encoded, offset, network_id)) + return fail(error, "truncated issuance descriptor network"); + parsed.network = UNDEFINED; + for (const network_type candidate : {MAINNET, TESTNET, STAGENET}) + { + if (network_id == get_config(candidate).NETWORK_ID) + { + parsed.network = candidate; + break; + } + } + + parsed.type = static_cast<asset_class>(encoded[offset++]); + if (!read_pod(encoded, offset, parsed.issuer_key) + || !read_pod(encoded, offset, parsed.issuance_nonce) + || !read_u64_le(encoded, offset, parsed.atomic_supply)) + return fail(error, "truncated issuance descriptor identity"); + parsed.display_decimals = encoded[offset++]; + if (!read_pod(encoded, offset, parsed.metadata_hash) + || !read_pod(encoded, offset, parsed.collection_id)) + return fail(error, "truncated issuance descriptor metadata"); + + uint16_t reference_size = 0; + if (!read_u16_le(encoded, offset, reference_size)) + return fail(error, "truncated issuance descriptor metadata length"); + if (reference_size > MAX_METADATA_REFERENCE_BYTES) + return fail(error, "metadata reference is too long"); + if (offset > encoded.size() || reference_size != encoded.size() - offset) + return fail(error, "issuance descriptor length is not canonical"); + parsed.metadata_reference.assign( + reinterpret_cast<const char*>(encoded.data() + offset), reference_size); + + if (!validate_issuance_descriptor(parsed, error)) + return false; + + std::vector<uint8_t> canonical; + if (!encode_issuance_descriptor(parsed, canonical, error) || canonical != encoded) + return fail(error, "issuance descriptor encoding is not canonical"); + + descriptor = std::move(parsed); + return true; + } + bool derive_asset_id(const issuance_descriptor& descriptor, crypto::hash& asset_id, std::string* error) { std::vector<uint8_t> encoded; diff --git a/src/cryptonote_basic/asset_types.h b/src/cryptonote_basic/asset_types.h index e24f61576..7f2fe0c99 100644 --- a/src/cryptonote_basic/asset_types.h +++ b/src/cryptonote_basic/asset_types.h @@ -48,6 +48,7 @@ namespace assets bool validate_issuance_descriptor(const issuance_descriptor& descriptor, std::string* error = nullptr); bool encode_issuance_descriptor(const issuance_descriptor& descriptor, std::vector<uint8_t>& encoded, std::string* error = nullptr); + bool decode_issuance_descriptor(const std::vector<uint8_t>& encoded, issuance_descriptor& descriptor, std::string* error = nullptr); bool derive_asset_id(const issuance_descriptor& descriptor, crypto::hash& asset_id, std::string* error = nullptr); bool derive_issuance_authorization_hash(const issuance_descriptor& descriptor, crypto::hash& message, std::string* error = nullptr); bool verify_issuance_authorization(const issuance_descriptor& descriptor, const crypto::signature& signature, std::string* error = nullptr); |
