From 1d0314c0d220231afc56193ba66b70234fa24092 Mon Sep 17 00:00:00 2001 From: Bitflash Date: Tue, 28 Jul 2026 12:25:29 -0300 Subject: btfaddr: reject malformed descriptors instead of throwing (#8) VerifyDescriptor() parses descriptors received from untrusted peers over Nostr relays. Only json::parse() was guarded; the field extractions (get, get) ran unguarded, so a descriptor with a wrong-typed field (e.g. a numeric "pubkey") threw an uncaught nlohmann::json::type_error -- a remotely triggerable crash vector. Validate each field's type up front and wrap the whole parse in try/catch so any malformed descriptor is rejected (return false), never thrown. --- src/btfaddr.cpp | 70 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 29 deletions(-) (limited to 'src/btfaddr.cpp') diff --git a/src/btfaddr.cpp b/src/btfaddr.cpp index 5f983bc..55bb6c5 100644 --- a/src/btfaddr.cpp +++ b/src/btfaddr.cpp @@ -214,35 +214,47 @@ bool VerifyDescriptor(void* ctxv, const std::string& jsonStr, const unsigned char expect_pubkey[32], Descriptor& out) { secp256k1_context* ctx = (secp256k1_context*)ctxv; - json v; - try { v = json::parse(jsonStr); } catch (...) { return false; } - if (!v.is_object()) return false; - if (!v.contains("pubkey") || !v.contains("enc") || !v.contains("meeting_node") - || !v.contains("created") || !v.contains("sig")) return false; - - unsigned char pubkey[32]; - if (!HexDecode(v["pubkey"].get(), pubkey, 32)) return false; - if (memcmp(pubkey, expect_pubkey, 32) != 0) return false; // descriptor is for a different key - - std::string enc = v["enc"].get(); - std::string meeting_node = v["meeting_node"].get(); - uint64_t created = v["created"].get(); - - unsigned char sig[64]; - if (!HexDecode(v["sig"].get(), sig, 64)) return false; - - unsigned char digest[32]; - DescriptorDigest(expect_pubkey, enc, meeting_node, created, digest); - - secp256k1_xonly_pubkey xpub; - if (!secp256k1_xonly_pubkey_parse(ctx, &xpub, expect_pubkey)) return false; - if (!secp256k1_schnorrsig_verify(ctx, sig, digest, 32, &xpub)) return false; - - memcpy(out.pubkey, expect_pubkey, 32); - out.enc = enc; - out.meeting_node = meeting_node; - out.created = created; - return true; + // Descriptors arrive from untrusted peers (via Nostr relays). Guard the whole + // parse: a malformed or wrong-typed field must be rejected, never throw. + try + { + json v = json::parse(jsonStr); + if (!v.is_object()) return false; + if (!v.contains("pubkey") || !v.contains("enc") || !v.contains("meeting_node") + || !v.contains("created") || !v.contains("sig")) return false; + // Reject wrong field types before extracting (json::get would otherwise throw). + if (!v["pubkey"].is_string() || !v["enc"].is_string() || + !v["meeting_node"].is_string() || !v["sig"].is_string() || + !v["created"].is_number_unsigned()) return false; + + unsigned char pubkey[32]; + if (!HexDecode(v["pubkey"].get(), pubkey, 32)) return false; + if (memcmp(pubkey, expect_pubkey, 32) != 0) return false; // descriptor is for a different key + + std::string enc = v["enc"].get(); + std::string meeting_node = v["meeting_node"].get(); + uint64_t created = v["created"].get(); + + unsigned char sig[64]; + if (!HexDecode(v["sig"].get(), sig, 64)) return false; + + unsigned char digest[32]; + DescriptorDigest(expect_pubkey, enc, meeting_node, created, digest); + + secp256k1_xonly_pubkey xpub; + if (!secp256k1_xonly_pubkey_parse(ctx, &xpub, expect_pubkey)) return false; + if (!secp256k1_schnorrsig_verify(ctx, sig, digest, 32, &xpub)) return false; + + memcpy(out.pubkey, expect_pubkey, 32); + out.enc = enc; + out.meeting_node = meeting_node; + out.created = created; + return true; + } + catch (...) + { + return false; // malformed / wrong-typed descriptor -> reject, don't crash + } } } // namespace btf -- cgit v1.2.3