mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-07-12 23:16:38 +02:00
389 lines
15 KiB
Python
389 lines
15 KiB
Python
import asyncio
|
|
|
|
from starlette.requests import Request
|
|
|
|
|
|
def _fresh_persona_state(tmp_path, monkeypatch):
|
|
from services.mesh import mesh_secure_storage, mesh_wormhole_identity, mesh_wormhole_persona
|
|
|
|
monkeypatch.setattr(mesh_secure_storage, "DATA_DIR", tmp_path)
|
|
monkeypatch.setattr(mesh_secure_storage, "MASTER_KEY_FILE", tmp_path / "wormhole_secure_store.key")
|
|
monkeypatch.setattr(mesh_wormhole_persona, "DATA_DIR", tmp_path)
|
|
monkeypatch.setattr(mesh_wormhole_persona, "PERSONA_FILE", tmp_path / "wormhole_persona.json")
|
|
monkeypatch.setattr(
|
|
mesh_wormhole_persona,
|
|
"LEGACY_DM_IDENTITY_FILE",
|
|
tmp_path / "wormhole_identity.json",
|
|
)
|
|
return mesh_wormhole_persona, mesh_wormhole_identity
|
|
|
|
|
|
def _request(path: str) -> Request:
|
|
return Request(
|
|
{
|
|
"type": "http",
|
|
"headers": [],
|
|
"client": ("test", 12345),
|
|
"method": "POST",
|
|
"path": path,
|
|
}
|
|
)
|
|
|
|
|
|
def test_transport_identity_is_separate_from_dm_identity(tmp_path, monkeypatch):
|
|
persona_mod, identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
dm_identity = identity_mod.bootstrap_wormhole_identity(force=True)
|
|
persona_state = persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
transport_identity = persona_state["transport_identity"]
|
|
|
|
assert dm_identity["node_id"]
|
|
assert transport_identity["node_id"]
|
|
assert dm_identity["node_id"] != transport_identity["node_id"]
|
|
assert dm_identity["public_key"] != transport_identity["public_key"]
|
|
|
|
|
|
def test_gate_anonymous_session_differs_from_transport_identity(tmp_path, monkeypatch):
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
transport_identity = persona_mod.get_transport_identity()
|
|
gate_identity = persona_mod.enter_gate_anonymously("journalists", rotate=True)
|
|
|
|
assert gate_identity["ok"] is True
|
|
assert gate_identity["identity"]["scope"] == "gate_session"
|
|
assert gate_identity["identity"]["gate_id"] == "journalists"
|
|
assert gate_identity["identity"]["node_id"] != transport_identity["node_id"]
|
|
|
|
|
|
def test_gate_access_proof_prefers_rotating_session_identity_over_persistent_persona(tmp_path, monkeypatch):
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
session = persona_mod.enter_gate_anonymously("journalists", rotate=True)["identity"]
|
|
persona = persona_mod.create_gate_persona("journalists", label="source-a")["identity"]
|
|
|
|
import main
|
|
|
|
proof_identity = main._resolve_gate_proof_identity("journalists")
|
|
|
|
assert proof_identity is not None
|
|
assert proof_identity["scope"] == "gate_session"
|
|
assert proof_identity["node_id"] == session["node_id"]
|
|
assert proof_identity["node_id"] != persona["node_id"]
|
|
|
|
|
|
def test_gate_access_proof_auto_enters_gate_when_identity_missing(tmp_path, monkeypatch):
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
|
|
import main
|
|
|
|
proof = asyncio.run(
|
|
main.api_wormhole_gate_proof(
|
|
_request("/api/wormhole/gate/proof"),
|
|
main.WormholeGateRequest(gate_id="journalists"),
|
|
)
|
|
)
|
|
active = persona_mod.get_active_gate_identity("journalists")
|
|
|
|
assert proof["ok"] is True
|
|
assert proof["gate_id"] == "journalists"
|
|
assert active["ok"] is True
|
|
assert active["source"] == "anonymous"
|
|
assert active["identity"]["scope"] == "gate_session"
|
|
assert active["identity"]["node_id"] == proof["node_id"]
|
|
|
|
|
|
def test_gate_identities_are_separate_from_root_identity(tmp_path, monkeypatch):
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
state = persona_mod.read_wormhole_persona_state()
|
|
root_identity = state["root_identity"]
|
|
gate_session = persona_mod.enter_gate_anonymously("journalists", rotate=True)["identity"]
|
|
gate_persona = persona_mod.create_gate_persona("journalists", label="source-a")["identity"]
|
|
|
|
assert root_identity["node_id"]
|
|
assert gate_session["node_id"] != root_identity["node_id"]
|
|
assert gate_session["public_key"] != root_identity["public_key"]
|
|
assert gate_persona["node_id"] != root_identity["node_id"]
|
|
assert gate_persona["public_key"] != root_identity["public_key"]
|
|
|
|
|
|
def test_gate_persona_activation_is_gate_local(tmp_path, monkeypatch):
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
first = persona_mod.create_gate_persona("sources", label="source-a")
|
|
second = persona_mod.create_gate_persona("leaks", label="source-a")
|
|
|
|
assert first["ok"] is True
|
|
assert second["ok"] is True
|
|
assert first["identity"]["gate_id"] == "sources"
|
|
assert second["identity"]["gate_id"] == "leaks"
|
|
assert first["identity"]["node_id"] != second["identity"]["node_id"]
|
|
|
|
active_sources = persona_mod.get_active_gate_identity("sources")
|
|
active_leaks = persona_mod.get_active_gate_identity("leaks")
|
|
assert active_sources["identity"]["persona_id"] == first["identity"]["persona_id"]
|
|
assert active_leaks["identity"]["persona_id"] == second["identity"]["persona_id"]
|
|
|
|
|
|
def test_gate_persona_duplicate_labels_get_unique_suffixes(tmp_path, monkeypatch):
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
first = persona_mod.create_gate_persona("sources", label="source-a")
|
|
second = persona_mod.create_gate_persona("sources", label="source-a")
|
|
third = persona_mod.create_gate_persona("sources", label="Source-A")
|
|
|
|
assert first["ok"] is True
|
|
assert second["ok"] is True
|
|
assert third["ok"] is True
|
|
assert first["identity"]["label"] == "source-a"
|
|
assert second["identity"]["label"] == "source-a-2"
|
|
assert third["identity"]["label"] == "Source-A-3"
|
|
|
|
|
|
def test_sign_public_event_uses_transport_identity(tmp_path, monkeypatch):
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
transport_identity = persona_mod.get_transport_identity()
|
|
signed = persona_mod.sign_public_wormhole_event(
|
|
event_type="message",
|
|
payload={
|
|
"message": "hello",
|
|
"destination": "broadcast",
|
|
"channel": "LongFast",
|
|
"priority": "normal",
|
|
"ephemeral": False,
|
|
},
|
|
)
|
|
|
|
assert signed["identity_scope"] == "transport"
|
|
assert signed["node_id"] == transport_identity["node_id"]
|
|
assert signed["public_key"] == transport_identity["public_key"]
|
|
|
|
|
|
def test_sign_root_event_uses_root_identity(tmp_path, monkeypatch):
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
root_identity = persona_mod.get_root_identity()
|
|
signed = persona_mod.sign_root_wormhole_event(
|
|
event_type="dm_prekey_root_attestation",
|
|
payload={"agent_id": "alias-1", "bundle_signature": "sig"},
|
|
)
|
|
|
|
assert signed["identity_scope"] == "root"
|
|
assert signed["node_id"] == root_identity["node_id"]
|
|
assert signed["public_key"] == root_identity["public_key"]
|
|
|
|
|
|
def test_sign_gate_event_uses_gate_session_identity(tmp_path, monkeypatch):
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
transport_identity = persona_mod.get_transport_identity()
|
|
signed = persona_mod.sign_gate_wormhole_event(
|
|
gate_id="journalists",
|
|
event_type="gate_message",
|
|
payload={
|
|
"gate": "journalists",
|
|
"epoch": 1,
|
|
"ciphertext": "opaque-source-drop",
|
|
"nonce": "nonce-j1",
|
|
"sender_ref": "gate-session-j1",
|
|
},
|
|
)
|
|
gate_identity = persona_mod.get_active_gate_identity("journalists")
|
|
|
|
assert signed["identity_scope"] == "gate_session"
|
|
assert signed["gate_id"] == "journalists"
|
|
assert signed["node_id"] == gate_identity["identity"]["node_id"]
|
|
assert signed["node_id"] != transport_identity["node_id"]
|
|
|
|
|
|
def test_leave_gate_forces_new_anonymous_session_on_reentry(tmp_path, monkeypatch):
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
first = persona_mod.enter_gate_anonymously("sources", rotate=True)
|
|
persona_mod.leave_gate("sources")
|
|
second = persona_mod.enter_gate_anonymously("sources", rotate=False)
|
|
|
|
assert first["identity"]["node_id"]
|
|
assert second["identity"]["node_id"]
|
|
assert first["identity"]["node_id"] != second["identity"]["node_id"]
|
|
|
|
|
|
def test_gate_session_rotation_uses_jitter_window_before_auto_swap(tmp_path, monkeypatch):
|
|
from services.config import get_settings
|
|
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
monkeypatch.setenv("MESH_GATE_SESSION_ROTATE_MSGS", "1")
|
|
monkeypatch.setenv("MESH_GATE_SESSION_ROTATE_JITTER_S", "120")
|
|
get_settings.cache_clear()
|
|
try:
|
|
now = {"value": 1_000.0}
|
|
monkeypatch.setattr(persona_mod.time, "time", lambda: now["value"])
|
|
monkeypatch.setattr(persona_mod.random, "uniform", lambda *_args, **_kwargs: 45.0)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
first = persona_mod.enter_gate_anonymously("sources", rotate=True)
|
|
persona_mod.sign_gate_wormhole_event(
|
|
gate_id="sources",
|
|
event_type="gate_message",
|
|
payload={
|
|
"gate": "sources",
|
|
"epoch": 1,
|
|
"ciphertext": "opaque",
|
|
"nonce": "nonce-1",
|
|
"sender_ref": "sender-ref-1",
|
|
"format": "mls1",
|
|
},
|
|
)
|
|
|
|
same = persona_mod.enter_gate_anonymously("sources", rotate=False)
|
|
scheduled = persona_mod.read_wormhole_persona_state()["gate_sessions"]["sources"]["_rotate_after"]
|
|
|
|
assert same["identity"]["node_id"] == first["identity"]["node_id"]
|
|
assert scheduled == 1_045.0
|
|
|
|
now["value"] = 1_046.0
|
|
rotated = persona_mod.enter_gate_anonymously("sources", rotate=False)
|
|
|
|
assert rotated["identity"]["node_id"] != first["identity"]["node_id"]
|
|
finally:
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def test_gate_enter_leave_do_not_emit_public_breadcrumbs(tmp_path, monkeypatch):
|
|
import main
|
|
from services.mesh import mesh_hashchain
|
|
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
append_called = {"count": 0}
|
|
|
|
def fake_append(**kwargs):
|
|
append_called["count"] += 1
|
|
return {"event_id": "unexpected"}
|
|
|
|
monkeypatch.setattr(mesh_hashchain.infonet, "append", fake_append)
|
|
|
|
body = main.WormholeGateRequest(gate_id="sources", rotate=True)
|
|
entered = asyncio.run(main.api_wormhole_gate_enter(_request("/api/wormhole/gate/enter"), body))
|
|
left = asyncio.run(
|
|
main.api_wormhole_gate_leave(
|
|
_request("/api/wormhole/gate/leave"),
|
|
main.WormholeGateRequest(gate_id="sources"),
|
|
)
|
|
)
|
|
|
|
assert entered["ok"] is True
|
|
assert left["ok"] is True
|
|
assert append_called["count"] == 0
|
|
|
|
|
|
def test_gate_enter_route_allows_private_control_only(tmp_path, monkeypatch):
|
|
import auth
|
|
import main
|
|
from httpx import ASGITransport, AsyncClient
|
|
from services import wormhole_supervisor
|
|
|
|
_fresh_persona_state(tmp_path, monkeypatch)
|
|
monkeypatch.setattr(auth, "_debug_mode_enabled", lambda: True)
|
|
monkeypatch.setattr(
|
|
wormhole_supervisor,
|
|
"get_wormhole_state",
|
|
lambda: {"configured": True, "ready": True, "arti_ready": False, "rns_ready": False},
|
|
)
|
|
|
|
async def _run():
|
|
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
|
|
response = await ac.post("/api/wormhole/gate/enter", json={"gate_id": "sources", "rotate": True})
|
|
return response.status_code, response.json()
|
|
|
|
status_code, payload = asyncio.run(_run())
|
|
|
|
assert status_code == 200
|
|
assert payload["ok"] is True
|
|
assert payload["identity"]["scope"] == "gate_session"
|
|
|
|
|
|
def test_clear_active_persona_reverts_gate_to_anonymous_session(tmp_path, monkeypatch):
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
persona_mod.enter_gate_anonymously("evidence", rotate=True)
|
|
created = persona_mod.create_gate_persona("evidence", label="reporter")
|
|
cleared = persona_mod.clear_active_gate_persona("evidence")
|
|
active = persona_mod.get_active_gate_identity("evidence")
|
|
|
|
assert created["identity"]["scope"] == "gate_persona"
|
|
assert cleared["ok"] is True
|
|
assert cleared["identity"]["scope"] == "gate_session"
|
|
assert active["source"] == "anonymous"
|
|
|
|
|
|
def test_sign_gate_event_uses_active_persona_when_selected(tmp_path, monkeypatch):
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
persona_mod.enter_gate_anonymously("ops", rotate=True)
|
|
created = persona_mod.create_gate_persona("ops", label="scribe")
|
|
signed = persona_mod.sign_gate_wormhole_event(
|
|
gate_id="ops",
|
|
event_type="gate_message",
|
|
payload={
|
|
"gate": "ops",
|
|
"epoch": 1,
|
|
"ciphertext": "opaque-persona-post",
|
|
"nonce": "nonce-o1",
|
|
"sender_ref": "persona-ops-1",
|
|
},
|
|
)
|
|
|
|
assert created["identity"]["persona_id"]
|
|
assert signed["identity_scope"] == "gate_persona"
|
|
assert signed["node_id"] == created["identity"]["node_id"]
|
|
|
|
|
|
def test_enter_gate_anonymously_clears_existing_active_persona(tmp_path, monkeypatch):
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
created = persona_mod.create_gate_persona("ops", label="scribe")
|
|
entered = persona_mod.enter_gate_anonymously("ops", rotate=True)
|
|
active = persona_mod.get_active_gate_identity("ops")
|
|
|
|
assert created["identity"]["scope"] == "gate_persona"
|
|
assert entered["identity"]["scope"] == "gate_session"
|
|
assert active["source"] == "anonymous"
|
|
assert active["identity"]["node_id"] == entered["identity"]["node_id"]
|
|
assert active["identity"]["node_id"] != created["identity"]["node_id"]
|
|
|
|
|
|
def test_sign_gate_event_rejects_cross_gate_payload_mismatch(tmp_path, monkeypatch):
|
|
persona_mod, _identity_mod = _fresh_persona_state(tmp_path, monkeypatch)
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
persona_mod.enter_gate_anonymously("ops", rotate=True)
|
|
signed = persona_mod.sign_gate_wormhole_event(
|
|
gate_id="ops",
|
|
event_type="gate_message",
|
|
payload={
|
|
"gate": "finance",
|
|
"epoch": 1,
|
|
"ciphertext": "opaque-cross-gate-post",
|
|
"nonce": "nonce-cross-1",
|
|
"sender_ref": "persona-finance-1",
|
|
},
|
|
)
|
|
|
|
assert signed["ok"] is False
|
|
assert signed["detail"] == "gate payload mismatch"
|