mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-05-08 10:24:48 +02:00
528 lines
18 KiB
Python
528 lines
18 KiB
Python
"""S10B DM-Shaped Cover Traffic With Mailbox TTL.
|
|
|
|
Tests:
|
|
- _send_cover_traffic emits private_dm-shaped RNSMessage
|
|
- Cover wire message uses private_dm-style message_id prefix and dandelion metadata
|
|
- Cover body has mailbox_key and envelope rather than pad/size
|
|
- Collecting real mailbox keys does not return cover entries
|
|
- Stale mailbox entries are pruned by TTL
|
|
- Legacy incoming cover_traffic is still handled safely
|
|
- S8A size/rate bounds are not regressed
|
|
"""
|
|
|
|
import base64
|
|
import json
|
|
import time
|
|
from types import SimpleNamespace
|
|
|
|
from services.config import Settings
|
|
from services.mesh.mesh_rns import (
|
|
RNSBridge,
|
|
_COVER_MAILBOX_PREFIX,
|
|
_DM_CT_FAMILY,
|
|
_blind_mailbox_key,
|
|
)
|
|
|
|
|
|
def _make_bridge() -> RNSBridge:
|
|
return RNSBridge()
|
|
|
|
|
|
# ── Cover emits private_dm-shaped message ─────────────────────────────
|
|
|
|
|
|
def test_cover_emits_private_dm_shape(monkeypatch):
|
|
"""_send_cover_traffic must produce a private_dm-typed RNSMessage."""
|
|
bridge = _make_bridge()
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(MESH_RNS_COVER_SIZE=512, MESH_RNS_MAX_PAYLOAD=8192),
|
|
)
|
|
|
|
sent: list[bytes] = []
|
|
|
|
def fake_pick_stem():
|
|
return "fake_peer"
|
|
|
|
def fake_send(peer, payload):
|
|
sent.append(payload)
|
|
return True
|
|
|
|
monkeypatch.setattr(bridge, "_pick_stem_peer", fake_pick_stem)
|
|
monkeypatch.setattr(bridge, "_send_to_peer", fake_send)
|
|
|
|
bridge._send_cover_traffic()
|
|
|
|
assert len(sent) == 1
|
|
msg = json.loads(sent[0])
|
|
assert msg["type"] == "private_dm", f"expected private_dm, got {msg['type']}"
|
|
|
|
|
|
def test_cover_message_id_prefix(monkeypatch):
|
|
"""Cover wire message must use private_dm-style message_id prefix."""
|
|
bridge = _make_bridge()
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(MESH_RNS_COVER_SIZE=512, MESH_RNS_MAX_PAYLOAD=8192),
|
|
)
|
|
sent: list[bytes] = []
|
|
monkeypatch.setattr(bridge, "_pick_stem_peer", lambda: "fake_peer")
|
|
monkeypatch.setattr(bridge, "_send_to_peer", lambda p, d: sent.append(d) or True)
|
|
|
|
bridge._send_cover_traffic()
|
|
|
|
msg = json.loads(sent[0])
|
|
message_id = msg["meta"]["message_id"]
|
|
assert message_id.startswith("private_dm:"), f"message_id should start with private_dm:, got {message_id}"
|
|
|
|
|
|
def test_cover_dandelion_metadata(monkeypatch):
|
|
"""Cover wire message must include dandelion stem metadata like real DMs."""
|
|
bridge = _make_bridge()
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(MESH_RNS_COVER_SIZE=512, MESH_RNS_MAX_PAYLOAD=8192,
|
|
MESH_RNS_DANDELION_HOPS=2),
|
|
)
|
|
sent: list[bytes] = []
|
|
monkeypatch.setattr(bridge, "_pick_stem_peer", lambda: "fake_peer")
|
|
monkeypatch.setattr(bridge, "_send_to_peer", lambda p, d: sent.append(d) or True)
|
|
|
|
bridge._send_cover_traffic()
|
|
|
|
msg = json.loads(sent[0])
|
|
dandelion = msg["meta"].get("dandelion", {})
|
|
assert dandelion.get("phase") == "stem"
|
|
assert dandelion.get("hops") == 0
|
|
assert "max_hops" in dandelion
|
|
|
|
|
|
def test_cover_originator_schedules_delayed_diffuse(monkeypatch):
|
|
"""Cover origination should mirror DM stem + delayed diffuse behavior."""
|
|
bridge = _make_bridge()
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(
|
|
MESH_RNS_COVER_SIZE=512,
|
|
MESH_RNS_MAX_PAYLOAD=8192,
|
|
MESH_RNS_DANDELION_HOPS=2,
|
|
MESH_RNS_DANDELION_DELAY_MS=400,
|
|
),
|
|
)
|
|
stem_sent: list[tuple[str, bytes]] = []
|
|
diffuse_sent: list[tuple[bytes, str | None]] = []
|
|
timer_delays: list[float] = []
|
|
|
|
class FakeTimer:
|
|
def __init__(self, delay, fn):
|
|
self.delay = delay
|
|
self.fn = fn
|
|
|
|
def start(self):
|
|
timer_delays.append(self.delay)
|
|
self.fn()
|
|
|
|
monkeypatch.setattr("services.mesh.mesh_rns.threading.Timer", FakeTimer)
|
|
monkeypatch.setattr(bridge, "_pick_stem_peer", lambda: "stem-peer")
|
|
monkeypatch.setattr(bridge, "_send_to_peer", lambda peer, payload: stem_sent.append((peer, payload)) or True)
|
|
monkeypatch.setattr(
|
|
bridge,
|
|
"_send_diffuse",
|
|
lambda payload, exclude=None: diffuse_sent.append((payload, exclude)) or 1,
|
|
)
|
|
|
|
bridge._send_cover_traffic()
|
|
|
|
assert len(stem_sent) == 1
|
|
assert len(diffuse_sent) == 1
|
|
assert timer_delays == [0.4]
|
|
assert diffuse_sent[0][1] == "stem-peer"
|
|
|
|
stem_msg = json.loads(stem_sent[0][1])
|
|
diffuse_msg = json.loads(diffuse_sent[0][0])
|
|
assert stem_msg["type"] == "private_dm"
|
|
assert diffuse_msg["type"] == "private_dm"
|
|
assert stem_msg["meta"]["dandelion"]["phase"] == "stem"
|
|
assert diffuse_msg["meta"]["dandelion"]["phase"] == "diffuse"
|
|
assert stem_msg["meta"]["message_id"] == diffuse_msg["meta"]["message_id"]
|
|
assert stem_msg["body"] == diffuse_msg["body"]
|
|
|
|
|
|
def test_cover_falls_back_to_diffuse_without_stem_peer(monkeypatch):
|
|
"""Without a stem peer, cover should mirror DM diffuse fallback behavior."""
|
|
bridge = _make_bridge()
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(MESH_RNS_COVER_SIZE=512, MESH_RNS_MAX_PAYLOAD=8192),
|
|
)
|
|
diffuse_sent: list[tuple[bytes, str | None]] = []
|
|
monkeypatch.setattr(bridge, "_pick_stem_peer", lambda: None)
|
|
monkeypatch.setattr(bridge, "_send_to_peer", lambda peer, payload: False)
|
|
monkeypatch.setattr(
|
|
bridge,
|
|
"_send_diffuse",
|
|
lambda payload, exclude=None: diffuse_sent.append((payload, exclude)) or 1,
|
|
)
|
|
|
|
bridge._send_cover_traffic()
|
|
|
|
assert len(diffuse_sent) == 1
|
|
assert diffuse_sent[0][1] is None
|
|
msg = json.loads(diffuse_sent[0][0])
|
|
assert msg["type"] == "private_dm"
|
|
assert msg["meta"]["dandelion"]["phase"] == "stem"
|
|
assert "mailbox_key" in msg["body"]
|
|
assert "envelope" in msg["body"]
|
|
|
|
|
|
def test_cover_body_has_mailbox_and_envelope(monkeypatch):
|
|
"""Cover body must have mailbox_key + envelope, not pad/size."""
|
|
bridge = _make_bridge()
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(MESH_RNS_COVER_SIZE=512, MESH_RNS_MAX_PAYLOAD=8192),
|
|
)
|
|
sent: list[bytes] = []
|
|
monkeypatch.setattr(bridge, "_pick_stem_peer", lambda: "fake_peer")
|
|
monkeypatch.setattr(bridge, "_send_to_peer", lambda p, d: sent.append(d) or True)
|
|
|
|
bridge._send_cover_traffic()
|
|
|
|
msg = json.loads(sent[0])
|
|
body = msg["body"]
|
|
assert "mailbox_key" in body, "cover body must have mailbox_key"
|
|
assert "envelope" in body, "cover body must have envelope"
|
|
assert isinstance(body["envelope"], dict)
|
|
envelope = body["envelope"]
|
|
assert "ciphertext" in envelope
|
|
assert "msg_id" in envelope
|
|
assert "pad" not in body, "cover body must not have legacy pad field"
|
|
assert "size" not in body, "cover body must not have legacy size field"
|
|
|
|
|
|
# ── Collecting real mailbox keys does not return cover entries ─────────
|
|
|
|
|
|
def test_collect_real_mailbox_excludes_cover(monkeypatch):
|
|
"""Collecting a real mailbox key must not surface cover entries."""
|
|
bridge = _make_bridge()
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(MESH_DM_MAILBOX_TTL_S=900),
|
|
)
|
|
|
|
real_key = "real-user-mailbox-key"
|
|
blinded_real = _blind_mailbox_key(real_key)
|
|
|
|
# Store a real DM.
|
|
bridge._store_private_dm(blinded_real, {
|
|
"msg_id": "real-msg-1",
|
|
"sender_id": "sender1",
|
|
"ciphertext": "data",
|
|
"timestamp": time.time(),
|
|
"delivery_class": "shared",
|
|
"sender_seal": "",
|
|
})
|
|
|
|
# Simulate cover entry arriving via the normal private_dm receive path.
|
|
cover_mailbox = f"{_COVER_MAILBOX_PREFIX}abc123"
|
|
blinded_cover = _blind_mailbox_key(cover_mailbox)
|
|
bridge._store_private_dm(blinded_cover, {
|
|
"msg_id": "cover-msg-1",
|
|
"sender_id": "",
|
|
"ciphertext": "cover-data",
|
|
"timestamp": time.time(),
|
|
"delivery_class": "shared",
|
|
"sender_seal": "",
|
|
})
|
|
|
|
# Collect real mailbox — should only get the real DM.
|
|
collected, _ = bridge.collect_private_dm([real_key])
|
|
assert len(collected) == 1
|
|
assert collected[0]["msg_id"] == "real-msg-1"
|
|
|
|
|
|
def test_cover_mailbox_key_is_synthetic(monkeypatch):
|
|
"""The cover mailbox target must use synthetic prefix so it never
|
|
collides with real agent-derived mailbox keys."""
|
|
bridge = _make_bridge()
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(MESH_RNS_COVER_SIZE=512, MESH_RNS_MAX_PAYLOAD=8192),
|
|
)
|
|
sent: list[bytes] = []
|
|
monkeypatch.setattr(bridge, "_pick_stem_peer", lambda: "fake_peer")
|
|
monkeypatch.setattr(bridge, "_send_to_peer", lambda p, d: sent.append(d) or True)
|
|
|
|
bridge._send_cover_traffic()
|
|
|
|
msg = json.loads(sent[0])
|
|
mailbox_key = msg["body"]["mailbox_key"]
|
|
# The on-wire key is blinded, but we verify it was derived from a
|
|
# synthetic key by checking it differs from any plausible real key blind.
|
|
# More importantly: the synthetic prefix makes real collection impossible
|
|
# because real agents never know the pre-image.
|
|
assert mailbox_key, "mailbox_key must be non-empty"
|
|
assert len(mailbox_key) == 64, "blinded mailbox_key should be 64-char hex"
|
|
|
|
|
|
# ── TTL pruning ───────────────────────────────────────────────────────
|
|
|
|
|
|
def test_stale_entries_pruned_by_ttl(monkeypatch):
|
|
"""Mailbox entries older than MESH_DM_MAILBOX_TTL_S must be pruned."""
|
|
bridge = _make_bridge()
|
|
ttl = 60
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(MESH_DM_MAILBOX_TTL_S=ttl),
|
|
)
|
|
|
|
blinded = _blind_mailbox_key("test-key")
|
|
|
|
# Store an entry with a stale timestamp.
|
|
stale_ts = time.time() - ttl - 10
|
|
bridge._store_private_dm(blinded, {
|
|
"msg_id": "stale-1",
|
|
"sender_id": "s",
|
|
"ciphertext": "c",
|
|
"timestamp": stale_ts,
|
|
"delivery_class": "shared",
|
|
"sender_seal": "",
|
|
})
|
|
|
|
# Store a fresh entry to trigger pruning.
|
|
bridge._store_private_dm(blinded, {
|
|
"msg_id": "fresh-1",
|
|
"sender_id": "s",
|
|
"ciphertext": "c",
|
|
"timestamp": time.time(),
|
|
"delivery_class": "shared",
|
|
"sender_seal": "",
|
|
})
|
|
|
|
# Only the fresh entry should remain.
|
|
with bridge._dm_lock:
|
|
items = bridge._dm_mailboxes.get(blinded, [])
|
|
assert len(items) == 1, f"expected 1 item after prune, got {len(items)}"
|
|
assert items[0]["msg_id"] == "fresh-1"
|
|
|
|
|
|
def test_ttl_prune_removes_empty_mailbox_keys(monkeypatch):
|
|
"""Pruning must remove mailbox keys that become empty."""
|
|
bridge = _make_bridge()
|
|
ttl = 30
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(MESH_DM_MAILBOX_TTL_S=ttl),
|
|
)
|
|
|
|
blinded = _blind_mailbox_key("prune-test")
|
|
|
|
# Insert a stale-only entry.
|
|
with bridge._dm_lock:
|
|
bridge._dm_mailboxes[blinded] = [{
|
|
"msg_id": "old",
|
|
"sender_id": "",
|
|
"ciphertext": "",
|
|
"timestamp": time.time() - ttl - 100,
|
|
"delivery_class": "shared",
|
|
"sender_seal": "",
|
|
"transport": "reticulum",
|
|
}]
|
|
|
|
# Store into a different key to trigger prune.
|
|
other_blinded = _blind_mailbox_key("other-key")
|
|
bridge._store_private_dm(other_blinded, {
|
|
"msg_id": "trigger",
|
|
"sender_id": "",
|
|
"ciphertext": "",
|
|
"timestamp": time.time(),
|
|
"delivery_class": "shared",
|
|
"sender_seal": "",
|
|
})
|
|
|
|
with bridge._dm_lock:
|
|
assert blinded not in bridge._dm_mailboxes, "empty mailbox key should be pruned"
|
|
|
|
|
|
# ── Legacy cover_traffic still handled safely ─────────────────────────
|
|
|
|
|
|
def test_legacy_cover_traffic_silently_dropped(monkeypatch):
|
|
"""Legacy incoming cover_traffic messages must still be silently dropped."""
|
|
bridge = _make_bridge()
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(),
|
|
)
|
|
|
|
# Simulate receiving a legacy cover_traffic message.
|
|
legacy_msg = json.dumps({
|
|
"type": "cover_traffic",
|
|
"body": {"pad": base64.b64encode(b"x" * 64).decode(), "size": 64},
|
|
"meta": {"message_id": "cover:legacy123", "ts": int(time.time())},
|
|
}).encode()
|
|
|
|
# _on_packet should silently return without error.
|
|
monkeypatch.setattr(bridge, "_seen", lambda mid: False)
|
|
bridge._on_packet(legacy_msg)
|
|
|
|
# No crash, no entries stored — pass.
|
|
with bridge._dm_lock:
|
|
assert len(bridge._dm_mailboxes) == 0
|
|
|
|
|
|
def test_authenticated_cover_drops_before_mailbox_persistence(monkeypatch):
|
|
bridge = _make_bridge()
|
|
bridge._identity = SimpleNamespace(private_key=b"transport-secret-cover")
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(MESH_RNS_COVER_AUTH_MARKER_ENABLE=True),
|
|
)
|
|
monkeypatch.setattr(bridge, "_seen", lambda _mid: False)
|
|
|
|
envelope = bridge._with_transport_auth(
|
|
{
|
|
"msg_id": "cover-auth-1",
|
|
"sender_id": "",
|
|
"ciphertext": base64.b64encode(b"x" * _DM_CT_FAMILY[0]).decode("ascii"),
|
|
"timestamp": time.time(),
|
|
"delivery_class": "shared",
|
|
"sender_seal": "",
|
|
},
|
|
cover=True,
|
|
)
|
|
|
|
payload = json.dumps(
|
|
{
|
|
"type": "private_dm",
|
|
"body": {"mailbox_key": _blind_mailbox_key("mailbox-cover-1"), "envelope": envelope},
|
|
"meta": {"message_id": "private_dm:cover-auth-1", "dandelion": {"phase": "diffuse"}},
|
|
}
|
|
).encode()
|
|
|
|
bridge._on_packet(payload)
|
|
|
|
with bridge._dm_lock:
|
|
assert bridge._dm_mailboxes == {}
|
|
|
|
|
|
def test_authenticated_non_cover_persists_after_mac_verify(monkeypatch):
|
|
bridge = _make_bridge()
|
|
bridge._identity = SimpleNamespace(private_key=b"transport-secret-real")
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(MESH_RNS_COVER_AUTH_MARKER_ENABLE=True),
|
|
)
|
|
monkeypatch.setattr(bridge, "_seen", lambda _mid: False)
|
|
|
|
envelope = bridge._with_transport_auth(
|
|
{
|
|
"msg_id": "real-auth-1",
|
|
"sender_id": "alice",
|
|
"ciphertext": base64.b64encode(b"y" * _DM_CT_FAMILY[0]).decode("ascii"),
|
|
"timestamp": time.time(),
|
|
"delivery_class": "shared",
|
|
"sender_seal": "",
|
|
},
|
|
cover=False,
|
|
)
|
|
mailbox = _blind_mailbox_key("mailbox-real-1")
|
|
payload = json.dumps(
|
|
{
|
|
"type": "private_dm",
|
|
"body": {"mailbox_key": mailbox, "envelope": envelope},
|
|
"meta": {"message_id": "private_dm:real-auth-1", "dandelion": {"phase": "diffuse"}},
|
|
}
|
|
).encode()
|
|
|
|
bridge._on_packet(payload)
|
|
|
|
with bridge._dm_lock:
|
|
assert bridge._dm_mailboxes[mailbox][0]["msg_id"] == "real-auth-1"
|
|
|
|
|
|
def test_malformed_cover_rejects_before_auth_verify_and_mailbox_growth(monkeypatch):
|
|
bridge = _make_bridge()
|
|
bridge._identity = SimpleNamespace(private_key=b"transport-secret-malformed")
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(MESH_RNS_COVER_AUTH_MARKER_ENABLE=True),
|
|
)
|
|
monkeypatch.setattr(bridge, "_seen", lambda _mid: False)
|
|
|
|
verify_calls = {"count": 0}
|
|
|
|
def fake_verify(*_args, **_kwargs):
|
|
verify_calls["count"] += 1
|
|
return True, False
|
|
|
|
monkeypatch.setattr(bridge, "_verify_transport_auth_block", fake_verify)
|
|
|
|
for index in range(50):
|
|
payload = json.dumps(
|
|
{
|
|
"type": "private_dm",
|
|
"body": {
|
|
"mailbox_key": _blind_mailbox_key(f"mailbox-malformed-{index}"),
|
|
"envelope": {
|
|
"msg_id": f"malformed-{index}",
|
|
"sender_id": "",
|
|
"ciphertext": base64.b64encode(b"not-a-grounded-bucket").decode("ascii"),
|
|
"timestamp": time.time(),
|
|
"delivery_class": "shared",
|
|
"sender_seal": "",
|
|
"transport_auth": base64.b64encode(b"bogus").decode("ascii"),
|
|
},
|
|
},
|
|
"meta": {"message_id": f"private_dm:malformed-{index}", "dandelion": {"phase": "diffuse"}},
|
|
}
|
|
).encode()
|
|
bridge._on_packet(payload)
|
|
|
|
assert verify_calls["count"] == 0
|
|
with bridge._dm_lock:
|
|
assert bridge._dm_mailboxes == {}
|
|
|
|
|
|
# ── S8A invariants not regressed ──────────────────────────────────────
|
|
|
|
|
|
def test_s8a_cover_size_floor_preserved():
|
|
"""Default MESH_RNS_COVER_SIZE must remain >= 512 (S8A invariant)."""
|
|
s = Settings()
|
|
assert s.MESH_RNS_COVER_SIZE >= 512
|
|
|
|
|
|
def test_s8a_cover_interval_independent_of_queue(monkeypatch):
|
|
"""Cover interval must not expand when batch queue has items (S8A)."""
|
|
bridge = _make_bridge()
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(MESH_RNS_COVER_INTERVAL_S=30),
|
|
)
|
|
monkeypatch.setattr(bridge, "_is_high_privacy", lambda: True)
|
|
|
|
baseline = bridge._cover_interval()
|
|
bridge._batch_queue = [{"fake": i} for i in range(20)]
|
|
with_queue = bridge._cover_interval()
|
|
assert with_queue == baseline
|
|
|
|
|
|
def test_s8a_cover_bounded_by_max_payload(monkeypatch):
|
|
"""Cover on-wire payload must not exceed MESH_RNS_MAX_PAYLOAD (S8A)."""
|
|
bridge = _make_bridge()
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_rns.get_settings",
|
|
lambda: Settings(MESH_RNS_COVER_SIZE=512, MESH_RNS_MAX_PAYLOAD=8192),
|
|
)
|
|
sent: list[bytes] = []
|
|
monkeypatch.setattr(bridge, "_pick_stem_peer", lambda: "fake_peer")
|
|
monkeypatch.setattr(bridge, "_send_to_peer", lambda p, d: sent.append(d) or True)
|
|
|
|
bridge._send_cover_traffic()
|
|
|
|
assert len(sent) == 1
|
|
assert len(sent[0]) <= 8192
|