diff options
| author | Bitflash <btf01@proton.me> | 2026-07-28 12:25:29 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-28 12:25:29 -0300 |
| commit | 1d0314c0d220231afc56193ba66b70234fa24092 (patch) | |
| tree | 622bfdb3f3f7d442b08d83fdf613d4ea717135d3 /src/btfaddr.cpp | |
| parent | ebb793b0c513e5ee0298228b0185b4bada842513 (diff) | |
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<std::string>, get<uint64_t>) 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.
Diffstat (limited to 'src/btfaddr.cpp')
| -rw-r--r-- | src/btfaddr.cpp | 70 |
1 files changed, 41 insertions, 29 deletions
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, | |||
| 214 | const unsigned char expect_pubkey[32], Descriptor& out) | 214 | const unsigned char expect_pubkey[32], Descriptor& out) |
| 215 | { | 215 | { |
| 216 | secp256k1_context* ctx = (secp256k1_context*)ctxv; | 216 | secp256k1_context* ctx = (secp256k1_context*)ctxv; |
| 217 | json v; | 217 | // Descriptors arrive from untrusted peers (via Nostr relays). Guard the whole |
| 218 | try { v = json::parse(jsonStr); } catch (...) { return false; } | 218 | // parse: a malformed or wrong-typed field must be rejected, never throw. |
| 219 | if (!v.is_object()) return false; | 219 | try |
| 220 | if (!v.contains("pubkey") || !v.contains("enc") || !v.contains("meeting_node") | 220 | { |
| 221 | || !v.contains("created") || !v.contains("sig")) return false; | 221 | json v = json::parse(jsonStr); |
| 222 | 222 | if (!v.is_object()) return false; | |
| 223 | unsigned char pubkey[32]; | 223 | if (!v.contains("pubkey") || !v.contains("enc") || !v.contains("meeting_node") |
| 224 | if (!HexDecode(v["pubkey"].get<std::string>(), pubkey, 32)) return false; | 224 | || !v.contains("created") || !v.contains("sig")) return false; |
| 225 | if (memcmp(pubkey, expect_pubkey, 32) != 0) return false; // descriptor is for a different key | 225 | // Reject wrong field types before extracting (json::get would otherwise throw). |
| 226 | 226 | if (!v["pubkey"].is_string() || !v["enc"].is_string() || | |
| 227 | std::string enc = v["enc"].get<std::string>(); | 227 | !v["meeting_node"].is_string() || !v["sig"].is_string() || |
| 228 | std::string meeting_node = v["meeting_node"].get<std::string>(); | 228 | !v["created"].is_number_unsigned()) return false; |
| 229 | uint64_t created = v["created"].get<uint64_t>(); | 229 | |
| 230 | 230 | unsigned char pubkey[32]; | |
| 231 | unsigned char sig[64]; | 231 | if (!HexDecode(v["pubkey"].get<std::string>(), pubkey, 32)) return false; |
| 232 | if (!HexDecode(v["sig"].get<std::string>(), sig, 64)) return false; | 232 | if (memcmp(pubkey, expect_pubkey, 32) != 0) return false; // descriptor is for a different key |
| 233 | 233 | ||
| 234 | unsigned char digest[32]; | 234 | std::string enc = v["enc"].get<std::string>(); |
| 235 | DescriptorDigest(expect_pubkey, enc, meeting_node, created, digest); | 235 | std::string meeting_node = v["meeting_node"].get<std::string>(); |
| 236 | 236 | uint64_t created = v["created"].get<uint64_t>(); | |
| 237 | secp256k1_xonly_pubkey xpub; | 237 | |
| 238 | if (!secp256k1_xonly_pubkey_parse(ctx, &xpub, expect_pubkey)) return false; | 238 | unsigned char sig[64]; |
| 239 | if (!secp256k1_schnorrsig_verify(ctx, sig, digest, 32, &xpub)) return false; | 239 | if (!HexDecode(v["sig"].get<std::string>(), sig, 64)) return false; |
| 240 | 240 | ||
| 241 | memcpy(out.pubkey, expect_pubkey, 32); | 241 | unsigned char digest[32]; |
| 242 | out.enc = enc; | 242 | DescriptorDigest(expect_pubkey, enc, meeting_node, created, digest); |
| 243 | out.meeting_node = meeting_node; | 243 | |
| 244 | out.created = created; | 244 | secp256k1_xonly_pubkey xpub; |
| 245 | return true; | 245 | if (!secp256k1_xonly_pubkey_parse(ctx, &xpub, expect_pubkey)) return false; |
| 246 | if (!secp256k1_schnorrsig_verify(ctx, sig, digest, 32, &xpub)) return false; | ||
| 247 | |||
| 248 | memcpy(out.pubkey, expect_pubkey, 32); | ||
| 249 | out.enc = enc; | ||
| 250 | out.meeting_node = meeting_node; | ||
| 251 | out.created = created; | ||
| 252 | return true; | ||
| 253 | } | ||
| 254 | catch (...) | ||
| 255 | { | ||
| 256 | return false; // malformed / wrong-typed descriptor -> reject, don't crash | ||
| 257 | } | ||
| 246 | } | 258 | } |
| 247 | 259 | ||
| 248 | } // namespace btf | 260 | } // namespace btf |