fix: MeshCore spec compliance audit and packet quality warnings
Audited against meshcore-spec and fixed all compliance gaps:
- Fix trace hash size encoding: use 1<<(flags&3) not (flags&3)+1
- Reject reserved path hash_size_code 3 per spec Section 3
- Minimum packet size 3 bytes (was 2)
- Add MAX_PACKET_PAYLOAD (184) and min payload (1) validation
- Tighten payload minimums: ANON_REQ≥51, encrypted≥20, group≥19,
multipart≥2
- Add node_type 0 ("none") to NODE_TYPES
- Fix node_type fallback display (was "node=type=14", now "node=0x0e")
Add packet quality warnings for likely-corrupt frames:
- decode_meshcore_packet returns (str, list[str]) with quality warnings
- ADVERT: flag bad node_type, bad UTF-8 in name, future/bad timestamps
- Combine content warnings with low-SNR signal at display layer
- Warnings stack: one flag = maybe, multiple = high confidence corrupt
Update test corpus and generators for tightened validation rules.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
10 files changed
+198
-82
No files matched your search
@@ -94,7 +94,7 @@ def advert_appdata(draw):
|
||||
name = data[8 if has_loc else 0:].decode("ascii", errors="replace")
|
||||
|
||||
app_data = bytes([flags]) + data
|
||||
node_label = NODE_TYPES.get(node_type, f"type={node_type}")
|
||||
node_label = NODE_TYPES.get(node_type, f"0x{node_type:02x}")
|
||||
return app_data, {"flags": flags, "node_type": node_label, "location": loc, "name": name}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ def valid_encrypted_peer_packet(draw):
|
||||
dst = draw(st.integers(min_value=0, max_value=255))
|
||||
src = draw(st.integers(min_value=0, max_value=255))
|
||||
mac = draw(st.binary(min_size=2, max_size=2))
|
||||
ct_len = draw(st.integers(min_value=0, max_value=64))
|
||||
ct_len = draw(st.integers(min_value=16, max_value=64).filter(lambda n: n % 16 == 0))
|
||||
ct = draw(st.binary(min_size=ct_len, max_size=ct_len))
|
||||
|
||||
hdr = bytes([mc_header(ptype, route_type)])
|
||||
@@ -188,7 +188,7 @@ def valid_group_packet(draw):
|
||||
hs, hops = draw(path_strategy())
|
||||
ch = draw(st.integers(min_value=0, max_value=255))
|
||||
mac = draw(st.binary(min_size=2, max_size=2))
|
||||
ct_len = draw(st.integers(min_value=0, max_value=64))
|
||||
ct_len = draw(st.integers(min_value=16, max_value=64).filter(lambda n: n % 16 == 0))
|
||||
ct = draw(st.binary(min_size=ct_len, max_size=ct_len))
|
||||
|
||||
hdr = bytes([mc_header(ptype, route_type)])
|
||||
@@ -220,7 +220,7 @@ def invalid_packet(draw):
|
||||
pkt, _ = draw(valid_ack_packet())
|
||||
cut = draw(st.integers(min_value=0, max_value=max(0, len(pkt) - 2)))
|
||||
packet = pkt[:cut]
|
||||
if len(packet) < 2:
|
||||
if len(packet) < 3:
|
||||
error = "too_short"
|
||||
else:
|
||||
error = "bad_framing"
|
||||
@@ -230,12 +230,18 @@ def invalid_packet(draw):
|
||||
ptype = draw(st.sampled_from(list(PAYLOAD_TYPES.keys())))
|
||||
hdr = bytes([mc_header(ptype, route_type)])
|
||||
# path_len claiming huge path
|
||||
bad_pl = draw(st.integers(min_value=0xC0, max_value=0xFF))
|
||||
# Use hash_size_code 3 (reserved, 0xC0-0xFF) or valid codes with huge counts
|
||||
bad_pl = draw(st.sampled_from([
|
||||
*range(0xC0, 0x100), # reserved hash_size_code 3
|
||||
0x3F, # 63 hops × 1 byte = 63 (valid encoding, will overflow data)
|
||||
0x60, # 32 hops × 2 bytes = 64 (valid encoding, will overflow data)
|
||||
0x95, # 21 hops × 3 bytes = 63 (valid encoding, will overflow data)
|
||||
]))
|
||||
tail = draw(st.binary(min_size=1, max_size=20))
|
||||
packet = hdr + bytes([bad_pl]) + tail
|
||||
error = "bad_framing"
|
||||
else: # random
|
||||
packet = draw(st.binary(min_size=2, max_size=50))
|
||||
packet = draw(st.binary(min_size=3, max_size=50))
|
||||
error = "not_meshcore"
|
||||
|
||||
header = packet[0] if packet else 0
|
||||
|
||||
Reference in new issue
Block a user