From e933bd36cee44b42d631c1c64e9f3df027be7ab0 Mon Sep 17 00:00:00 2001 From: Monzero Build System Date: Sat, 15 Aug 2026 21:55:20 +0100 Subject: Add strict canonical asset descriptor decoding --- src/cryptonote_basic/asset_types.cpp | 89 ++++++++++++++++++++++++++++++++++++ src/cryptonote_basic/asset_types.h | 1 + 2 files changed, 90 insertions(+) (limited to 'src/cryptonote_basic') 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 #include +#include + #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(value >> shift)); } + + template + bool read_pod(const std::vector& 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& source, size_t& offset, uint16_t& value) + { + if (offset > source.size() || 2 > source.size() - offset) + return false; + value = static_cast(source[offset]) + | static_cast(source[offset + 1]) << 8; + offset += 2; + return true; + } + + bool read_u64_le(const std::vector& 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(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& 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(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(encoded.data() + offset), reference_size); + + if (!validate_issuance_descriptor(parsed, error)) + return false; + + std::vector 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 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& encoded, std::string* error = nullptr); + bool decode_issuance_descriptor(const std::vector& 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); -- cgit v1.2.3