import asyncio import base64 import json def _embedded_gate_event_wire_size(gate_mls_mod, persona_id: str, gate_id: str, plaintext: str) -> int: from services.mesh.mesh_hashchain import build_gate_wire_ref from services.mesh.mesh_rns import RNSMessage binding = gate_mls_mod._sync_binding(gate_id) member = binding.members[persona_id] proof = { "proof_version": "embedded-proof-v1", "node_id": "!sb_embeddedproof", "public_key": "A" * 44, "public_key_algo": "Ed25519", "sequence": 7, "protocol_version": "infonet/2", "content_hash": "b" * 64, "transport_hash": "c" * 64, "signature": "d" * 128, } plaintext_with_proof = json.dumps( { "m": plaintext, "e": int(binding.epoch), "proof": proof, }, separators=(",", ":"), ensure_ascii=False, ) ciphertext = gate_mls_mod._privacy_client().encrypt_group_message( member.group_handle, plaintext_with_proof.encode("utf-8"), ) padded = gate_mls_mod._pad_ciphertext_raw(ciphertext) event = { "gate_contract_version": "gate-v2-embedded-origin-v1", "event_type": "gate_message", "timestamp": 1710000000, "event_id": "e" * 64, "payload": { "ciphertext": gate_mls_mod._b64(padded), "format": gate_mls_mod.MLS_GATE_FORMAT, "nonce": "n" * 16, "sender_ref": "s" * 16, "epoch": int(binding.epoch), }, } event["payload"]["gate_ref"] = build_gate_wire_ref( gate_id, event, peer_url="https://test.local" ) return len( RNSMessage( msg_type="gate_event", body={"event": event}, meta={"message_id": "mid", "dandelion": {"phase": "stem", "hops": 0, "max_hops": 3}}, ).encode() ) class _TestGateManager: """Minimal gate manager stub that returns a fixed per-gate secret.""" _SECRET = "test-gate-secret-for-envelope-encryption" def get_gate_secret(self, gate_id: str) -> str: return self._SECRET def get_envelope_policy(self, gate_id: str) -> str: return "envelope_recovery" def can_enter(self, sender_id: str, gate_id: str): return True, "ok" def record_message(self, gate_id: str): pass def _fresh_gate_state(tmp_path, monkeypatch): from services import wormhole_supervisor from services.config import get_settings from services.mesh import mesh_gate_mls, mesh_reputation, mesh_secure_storage, mesh_wormhole_persona monkeypatch.setenv("MESH_GATE_RECOVERY_ENVELOPE_ENABLE", "true") monkeypatch.setenv("MESH_GATE_RECOVERY_ENVELOPE_ENABLE_ACKNOWLEDGE", "true") get_settings.cache_clear() 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_gate_mls, "DATA_DIR", tmp_path) monkeypatch.setattr(mesh_gate_mls, "STATE_FILE", tmp_path / "wormhole_gate_mls.json") 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", ) monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional") monkeypatch.setattr( wormhole_supervisor, "get_wormhole_state", lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": False}, ) monkeypatch.setattr(mesh_reputation, "gate_manager", _TestGateManager(), raising=False) mesh_gate_mls.reset_gate_mls_state() return mesh_gate_mls, mesh_wormhole_persona def test_gate_message_schema_accepts_mls1_format(): from services.mesh.mesh_protocol import normalize_payload from services.mesh.mesh_schema import validate_event_payload payload = normalize_payload( "gate_message", { "gate": "infonet", "epoch": 1, "ciphertext": "ZmFrZQ==", "nonce": "bWxzMS1lbnZlbG9wZQ==", "sender_ref": "persona-1", "format": "mls1", }, ) assert validate_event_payload("gate_message", payload) == (True, "ok") def test_sender_ref_is_stable_for_same_identity_and_nonce(): from services.mesh import mesh_gate_mls identity = {"persona_id": "persona-alpha", "node_id": "!sb_unused"} seed = mesh_gate_mls._sender_ref_seed(identity) first = mesh_gate_mls._sender_ref(seed, "nonce-stable-1") second = mesh_gate_mls._sender_ref(seed, "nonce-stable-1") assert first assert first == second assert len(first) == 16 def test_sender_ref_changes_across_nonce_and_identity_boundaries(): from services.mesh import mesh_gate_mls first_seed = mesh_gate_mls._sender_ref_seed({"persona_id": "persona-alpha"}) second_seed = mesh_gate_mls._sender_ref_seed({"persona_id": "persona-beta"}) same_identity_base = mesh_gate_mls._sender_ref(first_seed, "nonce-one") same_identity_other_nonce = mesh_gate_mls._sender_ref(first_seed, "nonce-two") other_identity_same_nonce = mesh_gate_mls._sender_ref(second_seed, "nonce-one") assert same_identity_base != same_identity_other_nonce assert same_identity_base != other_identity_same_nonce def test_compose_and_decrypt_gate_message_round_trip_via_mls(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona("finance", label="scribe") composed = gate_mls_mod.compose_encrypted_gate_message("finance", "hello mls gate") decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity( gate_id="finance", epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), nonce=str(composed["nonce"]), sender_ref=str(composed["sender_ref"]), ) assert composed["ok"] is True assert composed["format"] == "mls1" assert composed["ciphertext"] != "hello mls gate" assert decrypted == { "ok": True, "gate_id": "finance", "epoch": 1, "plaintext": "hello mls gate", "identity_scope": "persona", } def test_decrypt_gate_message_recovers_hidden_reply_to_from_ciphertext(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "finance" persona_mod.bootstrap_wormhole_persona_state(force=True) sender = persona_mod.create_gate_persona(gate_id, label="sender") receiver = persona_mod.create_gate_persona(gate_id, label="receiver") persona_mod.activate_gate_persona(gate_id, sender["identity"]["persona_id"]) composed = gate_mls_mod.compose_encrypted_gate_message( gate_id, "hello hidden thread", reply_to="evt-parent-hidden", ) persona_mod.activate_gate_persona(gate_id, receiver["identity"]["persona_id"]) decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity( gate_id=gate_id, epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), nonce=str(composed["nonce"]), sender_ref=str(composed["sender_ref"]), ) assert decrypted["ok"] is True assert decrypted["plaintext"] == "hello hidden thread" assert decrypted["reply_to"] == "evt-parent-hidden" def test_export_gate_state_snapshot_returns_opaque_state_only(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona("finance", label="scribe") composed = gate_mls_mod.compose_encrypted_gate_message("finance", "opaque export") snapshot = gate_mls_mod.export_gate_state_snapshot("finance") assert composed["ok"] is True assert snapshot["ok"] is True assert snapshot["gate_id"] == "finance" assert int(snapshot["epoch"]) >= 1 assert isinstance(snapshot["members"], list) and snapshot["members"] assert all(int(member["group_handle"]) > 0 for member in snapshot["members"]) assert "rust_state_blob_b64" in snapshot and snapshot["rust_state_blob_b64"] assert base64.b64decode(snapshot["rust_state_blob_b64"]) serialized = json.dumps(snapshot) assert "opaque export" not in serialized assert "gate_envelope" not in serialized def test_export_gate_state_snapshot_includes_active_member_metadata(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) persona_mod.bootstrap_wormhole_persona_state(force=True) created = persona_mod.create_gate_persona("finance", label="scribe") snapshot = gate_mls_mod.export_gate_state_snapshot("finance") assert snapshot["ok"] is True assert snapshot["active_identity_scope"] == "persona" assert snapshot["active_persona_id"] == created["identity"]["persona_id"] assert snapshot["active_node_id"] == created["identity"]["node_id"] def test_sign_encrypted_gate_message_returns_ciphertext_only_signature_surface(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona("finance", label="scribe") composed = gate_mls_mod.compose_encrypted_gate_message("finance", "native sign target") signed = gate_mls_mod.sign_encrypted_gate_message( gate_id="finance", epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), nonce="native-sign-nonce", ) assert signed["ok"] is True assert signed["gate_id"] == "finance" assert signed["ciphertext"] == composed["ciphertext"] assert signed["nonce"] == "native-sign-nonce" assert signed["reply_to"] == "" assert signed["sender_ref"] assert "native sign target" not in json.dumps(signed) def test_sign_encrypted_gate_message_rejects_cleartext_reply_to_without_compat(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona("finance", label="scribe") composed = gate_mls_mod.compose_encrypted_gate_message("finance", "native sign target") signed = gate_mls_mod.sign_encrypted_gate_message( gate_id="finance", epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), nonce="native-sign-nonce", reply_to="evt-parent-1", ) assert signed == { "ok": False, "detail": "gate_encrypted_reply_to_hidden_required", "gate_id": "finance", "compat_reply_to": False, } def test_sign_encrypted_gate_message_allows_cleartext_reply_to_in_explicit_compat_mode(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona("finance", label="scribe") composed = gate_mls_mod.compose_encrypted_gate_message("finance", "native sign target") signed = gate_mls_mod.sign_encrypted_gate_message( gate_id="finance", epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), nonce="native-sign-nonce", reply_to="evt-parent-1", compat_reply_to=True, ) assert signed["ok"] is True assert signed["reply_to"] == "evt-parent-1" def test_sign_encrypted_gate_message_rejects_stale_epoch(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona("finance", label="scribe") composed = gate_mls_mod.compose_encrypted_gate_message("finance", "stale epoch") signed = gate_mls_mod.sign_encrypted_gate_message( gate_id="finance", epoch=int(composed["epoch"]) + 1, ciphertext=str(composed["ciphertext"]), nonce="native-sign-stale", ) assert signed == { "ok": False, "detail": "gate_state_stale", "gate_id": "finance", "current_epoch": int(composed["epoch"]), } def test_sign_encrypted_gate_message_with_recovery_plaintext_produces_envelope(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona("finance", label="scribe") composed = gate_mls_mod.compose_encrypted_gate_message("finance", "recoverable payload") signed = gate_mls_mod.sign_encrypted_gate_message( gate_id="finance", epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), nonce="native-sign-nonce", recovery_plaintext="recoverable payload", ) assert signed["ok"] is True assert signed["gate_envelope"] assert signed["envelope_hash"] assert "recoverable payload" not in json.dumps(signed) def test_compose_refuses_recoverable_gate_without_envelope(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona("finance", label="scribe") def fail_encrypt(*_args, **_kwargs): raise gate_mls_mod.GateSecretUnavailableError("missing test secret") monkeypatch.setattr(gate_mls_mod, "_gate_envelope_encrypt", fail_encrypt) composed = gate_mls_mod.compose_encrypted_gate_message("finance", "must not become sealed") assert composed == { "ok": False, "detail": "gate_envelope_required", "gate_id": "finance", } def test_local_operator_gate_mutation_routes_include_state_snapshot(tmp_path, monkeypatch): import auth import main from httpx import ASGITransport, AsyncClient gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) persona_mod.bootstrap_wormhole_persona_state(force=True) async def _run(): async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac: created = await ac.post( "/api/wormhole/gate/persona/create", json={"gate_id": "finance", "label": "scribe"}, headers={"X-Admin-Key": auth._current_admin_key()}, ) rotated = await ac.post( "/api/wormhole/gate/key/rotate", json={"gate_id": "finance", "reason": "unit_test"}, headers={"X-Admin-Key": auth._current_admin_key()}, ) return created.json(), rotated.json() try: created, rotated = asyncio.run(_run()) finally: gate_mls_mod.reset_gate_mls_state() assert created["ok"] is True assert created["gate_state_snapshot"]["ok"] is True assert created["gate_state_snapshot"]["gate_id"] == "finance" assert int(created["gate_state_snapshot"]["epoch"]) >= 1 assert rotated["ok"] is True assert rotated["gate_state_snapshot"]["ok"] is True assert rotated["gate_state_snapshot"]["gate_id"] == "finance" assert int(rotated["gate_state_snapshot"]["epoch"]) >= int( created["gate_state_snapshot"]["epoch"] ) def test_anonymous_gate_session_can_compose_and_decrypt_round_trip(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.enter_gate_anonymously("finance", rotate=True) status = gate_mls_mod.get_local_gate_key_status("finance") composed = gate_mls_mod.compose_encrypted_gate_message("finance", "hello from anonymous gate") decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity( gate_id="finance", epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), nonce=str(composed["nonce"]), sender_ref=str(composed["sender_ref"]), ) assert status["ok"] is True assert status["identity_scope"] == "anonymous" assert status["has_local_access"] is True assert composed["ok"] is True assert composed["identity_scope"] == "anonymous" assert decrypted == { "ok": True, "gate_id": "finance", "epoch": 1, "plaintext": "hello from anonymous gate", "identity_scope": "anonymous", } def test_self_echo_decrypt_uses_local_plaintext_cache_fast_path(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona("finance", label="scribe") composed = gate_mls_mod.compose_encrypted_gate_message("finance", "cache hit") def fail_sync(_gate_id: str): raise AssertionError("self-echo cache should bypass MLS sync/decrypt") monkeypatch.setattr(gate_mls_mod, "_sync_binding", fail_sync) decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity( gate_id="finance", epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), nonce=str(composed["nonce"]), sender_ref=str(composed["sender_ref"]), ) assert decrypted == { "ok": True, "gate_id": "finance", "epoch": 1, "plaintext": "cache hit", "identity_scope": "persona", } def test_ordinary_gate_decrypt_does_not_stamp_plaintext_by_default(tmp_path, monkeypatch): from services.mesh import mesh_hashchain gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "finance" persona_mod.bootstrap_wormhole_persona_state(force=True) sender = persona_mod.create_gate_persona(gate_id, label="sender") receiver = persona_mod.create_gate_persona(gate_id, label="receiver") persona_mod.activate_gate_persona(gate_id, sender["identity"]["persona_id"]) composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "no durable plaintext") stored = mesh_hashchain.gate_store.append( gate_id, { "event_type": "gate_message", "timestamp": 1, "payload": { "gate": gate_id, "ciphertext": composed["ciphertext"], "nonce": composed["nonce"], "sender_ref": composed["sender_ref"], "format": composed["format"], }, }, ) persona_mod.activate_gate_persona(gate_id, receiver["identity"]["persona_id"]) decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity( gate_id=gate_id, epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), nonce=str(composed["nonce"]), sender_ref=str(composed["sender_ref"]), event_id=str(stored["event_id"]), ) assert decrypted["ok"] is True assert decrypted["plaintext"] == "no durable plaintext" assert mesh_hashchain.gate_store.lookup_local_plaintext(gate_id, stored["event_id"]) is None persisted = mesh_hashchain.gate_store.get_event(stored["event_id"]) assert persisted is not None assert "_local_plaintext" not in (persisted.get("payload") or {}) def test_recovery_envelope_read_decrypts_without_plaintext_persistence(tmp_path, monkeypatch): from services.mesh import mesh_hashchain gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "finance" persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona(gate_id, label="sender") composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "recovery plaintext") stored = mesh_hashchain.gate_store.append( gate_id, { "event_type": "gate_message", "timestamp": 1, "payload": { "gate": gate_id, "ciphertext": composed["ciphertext"], "nonce": composed["nonce"], "sender_ref": composed["sender_ref"], "format": composed["format"], "gate_envelope": composed.get("gate_envelope", ""), "envelope_hash": composed.get("envelope_hash", ""), }, }, ) decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity( gate_id=gate_id, epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), nonce=str(composed["nonce"]), gate_envelope=str(composed.get("gate_envelope", "") or ""), envelope_hash=str(composed.get("envelope_hash", "") or ""), recovery_envelope=True, event_id=str(stored["event_id"]), ) assert decrypted["ok"] is True assert decrypted["plaintext"] == "recovery plaintext" assert mesh_hashchain.gate_store.lookup_local_plaintext(gate_id, stored["event_id"]) is None def test_gate_plaintext_persist_opt_in_is_retired_no_plaintext_stamp(tmp_path, monkeypatch): from services.config import get_settings from services.mesh import mesh_hashchain monkeypatch.setenv("MESH_GATE_PLAINTEXT_PERSIST", "true") monkeypatch.setenv("MESH_GATE_PLAINTEXT_PERSIST_ACKNOWLEDGE", "true") get_settings.cache_clear() gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "finance" persona_mod.bootstrap_wormhole_persona_state(force=True) sender = persona_mod.create_gate_persona(gate_id, label="sender") receiver = persona_mod.create_gate_persona(gate_id, label="receiver") persona_mod.activate_gate_persona(gate_id, sender["identity"]["persona_id"]) composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "persisted plaintext") stored = mesh_hashchain.gate_store.append( gate_id, { "event_type": "gate_message", "timestamp": 1, "payload": { "gate": gate_id, "ciphertext": composed["ciphertext"], "nonce": composed["nonce"], "sender_ref": composed["sender_ref"], "format": composed["format"], }, }, ) persona_mod.activate_gate_persona(gate_id, receiver["identity"]["persona_id"]) decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity( gate_id=gate_id, epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), nonce=str(composed["nonce"]), sender_ref=str(composed["sender_ref"]), event_id=str(stored["event_id"]), ) assert decrypted["ok"] is True assert decrypted["plaintext"] == "persisted plaintext" assert mesh_hashchain.gate_store.lookup_local_plaintext(gate_id, stored["event_id"]) is None get_settings.cache_clear() def test_verifier_open_does_not_require_active_gate_persona(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "finance" persona_mod.bootstrap_wormhole_persona_state(force=True) first = persona_mod.create_gate_persona(gate_id, label="first") second = persona_mod.create_gate_persona(gate_id, label="second") persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"]) composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "verifier open") assert composed["ok"] is True persona_mod.enter_gate_anonymously(gate_id, rotate=True) opened = gate_mls_mod.open_gate_ciphertext_for_verifier( gate_id=gate_id, epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), format=str(composed["format"]), ) assert opened["ok"] is True assert opened["plaintext"] == "verifier open" assert opened["identity_scope"] == "verifier" assert opened["opened_by_persona_id"] in { first["identity"]["persona_id"], second["identity"]["persona_id"], } def test_verifier_open_does_not_use_self_echo_cache(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "finance" persona_mod.bootstrap_wormhole_persona_state(force=True) first = persona_mod.create_gate_persona(gate_id, label="first") second = persona_mod.create_gate_persona(gate_id, label="second") persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"]) composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "no cache authority") assert composed["ok"] is True monkeypatch.setattr( gate_mls_mod, "_peek_cached_plaintext", lambda *_args, **_kwargs: (_ for _ in ()).throw(AssertionError("verifier must not peek cache")), ) monkeypatch.setattr( gate_mls_mod, "_consume_cached_plaintext", lambda *_args, **_kwargs: (_ for _ in ()).throw(AssertionError("verifier must not consume cache")), ) monkeypatch.setattr(gate_mls_mod, "_active_gate_persona", lambda *_args, **_kwargs: None) opened = gate_mls_mod.open_gate_ciphertext_for_verifier( gate_id=gate_id, epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), format=str(composed["format"]), ) assert opened == { "ok": True, "gate_id": gate_id, "epoch": 1, "plaintext": "no cache authority", "opened_by_persona_id": second["identity"]["persona_id"], "identity_scope": "verifier", } def test_removed_member_cannot_decrypt_new_messages(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "opsec-lab" persona_mod.bootstrap_wormhole_persona_state(force=True) first = persona_mod.create_gate_persona(gate_id, label="first") second = persona_mod.create_gate_persona(gate_id, label="second") persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"]) before_removal = gate_mls_mod.compose_encrypted_gate_message(gate_id, "before removal") persona_mod.activate_gate_persona(gate_id, second["identity"]["persona_id"]) readable_before = gate_mls_mod.decrypt_gate_message_for_local_identity( gate_id=gate_id, epoch=int(before_removal["epoch"]), ciphertext=str(before_removal["ciphertext"]), nonce=str(before_removal["nonce"]), sender_ref=str(before_removal["sender_ref"]), ) persona_mod.retire_gate_persona(gate_id, second["identity"]["persona_id"]) persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"]) after_removal = gate_mls_mod.compose_encrypted_gate_message(gate_id, "after removal") persona_mod.enter_gate_anonymously(gate_id, rotate=True) blocked_after = gate_mls_mod.decrypt_gate_message_for_local_identity( gate_id=gate_id, epoch=int(after_removal["epoch"]), ciphertext=str(after_removal["ciphertext"]), nonce=str(after_removal["nonce"]), sender_ref=str(after_removal["sender_ref"]), ) assert readable_before["ok"] is True assert readable_before["plaintext"] == "before removal" assert blocked_after == { "ok": True, "gate_id": gate_id, "epoch": int(after_removal["epoch"]), "plaintext": "after removal", "identity_scope": "anonymous", } def test_gate_mls_state_survives_simulated_restart(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "infonet" persona_mod.bootstrap_wormhole_persona_state(force=True) first = persona_mod.create_gate_persona(gate_id, label="first") second = persona_mod.create_gate_persona(gate_id, label="second") persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"]) initial = gate_mls_mod.compose_encrypted_gate_message(gate_id, "before restart") gate_mls_mod.reset_gate_mls_state() persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"]) after_restart = gate_mls_mod.compose_encrypted_gate_message(gate_id, "after restart") persona_mod.activate_gate_persona(gate_id, second["identity"]["persona_id"]) decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity( gate_id=gate_id, epoch=int(after_restart["epoch"]), ciphertext=str(after_restart["ciphertext"]), nonce=str(after_restart["nonce"]), sender_ref=str(after_restart["sender_ref"]), ) assert initial["ok"] is True assert after_restart["ok"] is True assert after_restart["epoch"] == initial["epoch"] assert decrypted["ok"] is True assert decrypted["plaintext"] == "after restart" def test_pre_restart_gate_message_fails_to_decrypt_after_reset(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "restart-blackout" persona_mod.bootstrap_wormhole_persona_state(force=True) first = persona_mod.create_gate_persona(gate_id, label="first") second = persona_mod.create_gate_persona(gate_id, label="second") persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"]) before_reset = gate_mls_mod.compose_encrypted_gate_message(gate_id, "before reset") assert before_reset["ok"] is True persona_mod.activate_gate_persona(gate_id, second["identity"]["persona_id"]) readable_before = gate_mls_mod.decrypt_gate_message_for_local_identity( gate_id=gate_id, epoch=int(before_reset["epoch"]), ciphertext=str(before_reset["ciphertext"]), nonce=str(before_reset["nonce"]), sender_ref=str(before_reset["sender_ref"]), ) assert readable_before["ok"] is True assert readable_before["plaintext"] == "before reset" gate_mls_mod.reset_gate_mls_state() persona_mod.activate_gate_persona(gate_id, second["identity"]["persona_id"]) blocked_after = gate_mls_mod.decrypt_gate_message_for_local_identity( gate_id=gate_id, epoch=int(before_reset["epoch"]), ciphertext=str(before_reset["ciphertext"]), nonce=str(before_reset["nonce"]), sender_ref=str(before_reset["sender_ref"]), ) assert blocked_after == { "ok": False, "detail": "gate_mls_decrypt_failed", } def test_embedded_proof_budget_exceeds_rns_limit_before_6144_bucket_for_large_messages(tmp_path, monkeypatch): from services.config import get_settings gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "budget-gate" persona_mod.bootstrap_wormhole_persona_state(force=True) first = persona_mod.create_gate_persona(gate_id, label="first") persona_id = first["identity"]["persona_id"] persona_mod.activate_gate_persona(gate_id, persona_id) medium_wire = _embedded_gate_event_wire_size(gate_mls_mod, persona_id, gate_id, "x" * 1000) large_wire = _embedded_gate_event_wire_size(gate_mls_mod, persona_id, gate_id, "x" * 2000) assert medium_wire < get_settings().MESH_RNS_MAX_PAYLOAD assert large_wire > get_settings().MESH_RNS_MAX_PAYLOAD def test_sync_binding_skips_persist_when_membership_is_unchanged(tmp_path, monkeypatch): gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "quiet-room" persona_mod.bootstrap_wormhole_persona_state(force=True) first = persona_mod.create_gate_persona(gate_id, label="first") second = persona_mod.create_gate_persona(gate_id, label="second") persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"]) composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "steady state") persist_calls = [] original_persist = gate_mls_mod._persist_binding def track_persist(binding): persist_calls.append(binding.gate_id) return original_persist(binding) monkeypatch.setattr(gate_mls_mod, "_persist_binding", track_persist) persona_mod.activate_gate_persona(gate_id, second["identity"]["persona_id"]) decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity( gate_id=gate_id, epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), nonce=str(composed["nonce"]), sender_ref=str(composed["sender_ref"]), ) assert decrypted["ok"] is True assert decrypted["plaintext"] == "steady state" assert persist_calls == [] def test_tampered_binding_is_rejected_on_sync(tmp_path, monkeypatch, caplog): from services.mesh.mesh_local_custody import read_sensitive_domain_json, write_sensitive_domain_json import logging gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "cryptography" persona_mod.bootstrap_wormhole_persona_state(force=True) persona = persona_mod.create_gate_persona(gate_id, label="scribe") composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "tamper target") assert composed["ok"] is True stored = read_sensitive_domain_json( gate_mls_mod.STATE_DOMAIN, gate_mls_mod.STATE_FILENAME, gate_mls_mod._default_binding_store, custody_scope=gate_mls_mod.STATE_CUSTODY_SCOPE, ) persona_id = persona["identity"]["persona_id"] stored["gates"][gate_id]["members"][persona_id]["binding_signature"] = "00" * 64 write_sensitive_domain_json( gate_mls_mod.STATE_DOMAIN, gate_mls_mod.STATE_FILENAME, stored, custody_scope=gate_mls_mod.STATE_CUSTODY_SCOPE, ) gate_mls_mod.reset_gate_mls_state() with caplog.at_level(logging.WARNING): retry = gate_mls_mod.compose_encrypted_gate_message(gate_id, "should rebuild") assert retry["ok"] is True assert "corrupted binding for gate#" in caplog.text.lower() assert "member persona#" in caplog.text.lower() def test_mls_compose_allows_public_degraded_for_local_preparation(tmp_path, monkeypatch): from services import wormhole_supervisor gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona("finance", label="scribe") monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "public_degraded") result = gate_mls_mod.compose_encrypted_gate_message("finance", "prepare locally") assert result["ok"] is True assert result["format"] == "mls1" assert result["ciphertext"] assert result["sender_id"] def test_backend_local_gate_compose_post_encrypt_before_storage_but_mls_decrypt_stays_retired( tmp_path, monkeypatch ): import main import auth from httpx import ASGITransport, AsyncClient from services import wormhole_supervisor gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "infonet" persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona(gate_id, label="scribe") monkeypatch.setattr(auth, "_debug_mode_enabled", lambda: True) admin_headers = {"X-Admin-Key": auth._current_admin_key()} monkeypatch.setattr( wormhole_supervisor, "get_wormhole_state", lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": False}, ) composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "field report") async def _run(): async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac: compose_response = await ac.post( "/api/wormhole/gate/message/compose", json={"gate_id": gate_id, "plaintext": "field report", "compat_plaintext": True}, headers=admin_headers, ) send_response = await ac.post( "/api/wormhole/gate/message/post", json={"gate_id": gate_id, "plaintext": "field report", "compat_plaintext": True}, headers=admin_headers, ) decrypt_response = await ac.post( "/api/wormhole/gate/message/decrypt", json={ "gate_id": gate_id, "epoch": composed["epoch"], "ciphertext": composed["ciphertext"], "nonce": composed["nonce"], "sender_ref": composed["sender_ref"], "format": composed["format"], "compat_decrypt": True, }, headers=admin_headers, ) return compose_response.json(), send_response.json(), decrypt_response.json() try: compose_result, send_result, decrypt_result = asyncio.run(_run()) finally: gate_mls_mod.reset_gate_mls_state() assert compose_result["ok"] is True assert compose_result["gate_id"] == gate_id assert compose_result["ciphertext"] assert compose_result["gate_envelope"] assert send_result["ok"] is True assert send_result["gate_id"] == gate_id assert decrypt_result == { "ok": False, "detail": "gate_backend_decrypt_recovery_only", "gate_id": gate_id, "compat_requested": True, "compat_effective": False, } def test_backend_gate_decrypt_requires_recovery_for_mls_payloads(tmp_path, monkeypatch): import main import auth from routers import wormhole as wormhole_router from httpx import ASGITransport, AsyncClient from services import wormhole_supervisor gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "decrypt-policy-lab" persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona(gate_id, label="scribe") monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional") monkeypatch.setattr( wormhole_supervisor, "get_wormhole_state", lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": False}, ) composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "compat must be explicit") async def _run(): async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac: response = await ac.post( "/api/wormhole/gate/message/decrypt", json={ "gate_id": gate_id, "epoch": composed["epoch"], "ciphertext": composed["ciphertext"], "nonce": composed["nonce"], "sender_ref": composed["sender_ref"], "format": composed["format"], }, headers={"X-Admin-Key": auth._current_admin_key()}, ) return response.json() try: result = asyncio.run(_run()) finally: gate_mls_mod.reset_gate_mls_state() assert result == { "ok": False, "detail": "gate_backend_decrypt_recovery_only", "gate_id": gate_id, "compat_requested": False, "compat_effective": False, } def test_backend_gate_plaintext_compose_is_local_only(tmp_path, monkeypatch): import main import auth from routers import wormhole as wormhole_router from httpx import ASGITransport, AsyncClient from services import wormhole_supervisor gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "plaintext-policy-lab" persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona(gate_id, label="scribe") monkeypatch.setattr(auth, "_debug_mode_enabled", lambda: True) monkeypatch.setattr( wormhole_supervisor, "get_wormhole_state", lambda: {"configured": True, "ready": True, "arti_ready": True, "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/message/compose", json={ "gate_id": gate_id, "plaintext": "compat must be explicit", }, headers={"X-Admin-Key": auth._current_admin_key()}, ) return response.json() try: result = asyncio.run(_run()) finally: gate_mls_mod.reset_gate_mls_state() assert result["ok"] is True assert result["gate_id"] == gate_id assert result["ciphertext"] assert result["gate_envelope"] def test_backend_encrypted_gate_sign_requires_hidden_reply_to_or_explicit_compat(tmp_path, monkeypatch): import auth import main from httpx import ASGITransport, AsyncClient from services import wormhole_supervisor gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "encrypted-reply-guard-lab" persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona(gate_id, label="scribe") monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional") monkeypatch.setattr( wormhole_supervisor, "get_wormhole_state", lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": False}, ) composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "hidden reply_to only") async def _run(): async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac: blocked = await ac.post( "/api/wormhole/gate/message/sign-encrypted", json={ "gate_id": gate_id, "epoch": composed["epoch"], "ciphertext": composed["ciphertext"], "nonce": "native-sign-nonce", "reply_to": "evt-parent-1", }, headers={"X-Admin-Key": auth._current_admin_key()}, ) allowed = await ac.post( "/api/wormhole/gate/message/sign-encrypted", json={ "gate_id": gate_id, "epoch": composed["epoch"], "ciphertext": composed["ciphertext"], "nonce": "native-sign-nonce-compat", "reply_to": "evt-parent-1", "compat_reply_to": True, }, headers={"X-Admin-Key": auth._current_admin_key()}, ) return blocked.json(), allowed.json() try: blocked, allowed = asyncio.run(_run()) finally: gate_mls_mod.reset_gate_mls_state() assert blocked == { "ok": False, "detail": "gate_encrypted_reply_to_hidden_required", "gate_id": gate_id, "compat_reply_to": False, } assert allowed["ok"] is True assert allowed["reply_to"] == "evt-parent-1" def test_backend_encrypted_gate_post_requires_hidden_reply_to_or_explicit_compat(tmp_path, monkeypatch): import auth import main from httpx import ASGITransport, AsyncClient from services import wormhole_supervisor gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "encrypted-post-guard-lab" persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona(gate_id, label="scribe") monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional") monkeypatch.setattr( wormhole_supervisor, "get_wormhole_state", lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": False}, ) composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "hidden reply_to only") async def _run(): async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac: response = await ac.post( "/api/wormhole/gate/message/post-encrypted", json={ "gate_id": gate_id, "sender_id": composed["sender_id"], "public_key": composed["public_key"], "public_key_algo": composed["public_key_algo"], "signature": composed["signature"], "sequence": composed["sequence"], "protocol_version": composed["protocol_version"], "epoch": composed["epoch"], "ciphertext": composed["ciphertext"], "nonce": composed["nonce"], "sender_ref": composed["sender_ref"], "format": composed["format"], "gate_envelope": composed.get("gate_envelope", ""), "envelope_hash": composed.get("envelope_hash", ""), "reply_to": "evt-parent-1", }, headers={"X-Admin-Key": auth._current_admin_key()}, ) return response.json() try: result = asyncio.run(_run()) finally: gate_mls_mod.reset_gate_mls_state() assert result == { "ok": False, "detail": "gate_encrypted_reply_to_hidden_required", "gate_id": gate_id, "compat_reply_to": False, } def test_receive_only_mls_decrypt_locks_gate_format(tmp_path, monkeypatch): from services.mesh.mesh_secure_storage import read_domain_json, write_domain_json gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "receive-only-lab" persona_mod.bootstrap_wormhole_persona_state(force=True) first = persona_mod.create_gate_persona(gate_id, label="sender") second = persona_mod.create_gate_persona(gate_id, label="receiver") persona_mod.activate_gate_persona(gate_id, first["identity"]["persona_id"]) composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "receiver should lock gate") stored = read_domain_json( gate_mls_mod.STATE_DOMAIN, gate_mls_mod.STATE_FILENAME, gate_mls_mod._default_binding_store, ) stored.setdefault("gate_format_locks", {}).pop(gate_id, None) write_domain_json(gate_mls_mod.STATE_DOMAIN, gate_mls_mod.STATE_FILENAME, stored) assert gate_mls_mod.is_gate_locked_to_mls(gate_id) is True persona_mod.activate_gate_persona(gate_id, second["identity"]["persona_id"]) decrypted = gate_mls_mod.decrypt_gate_message_for_local_identity( gate_id=gate_id, epoch=int(composed["epoch"]), ciphertext=str(composed["ciphertext"]), nonce=str(composed["nonce"]), sender_ref=str(composed["sender_ref"]), ) assert decrypted["ok"] is True assert decrypted["plaintext"] == "receiver should lock gate" assert gate_mls_mod.is_gate_locked_to_mls(gate_id) is True def test_mls_locked_gate_rejects_legacy_g1_decrypt(tmp_path, monkeypatch): import main import auth from httpx import ASGITransport, AsyncClient from services import wormhole_supervisor gate_mls_mod, persona_mod = _fresh_gate_state(tmp_path, monkeypatch) gate_id = "lockout-lab" persona_mod.bootstrap_wormhole_persona_state(force=True) persona_mod.create_gate_persona(gate_id, label="scribe") monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional") monkeypatch.setattr( wormhole_supervisor, "get_wormhole_state", lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": False}, ) composed = gate_mls_mod.compose_encrypted_gate_message(gate_id, "mls only") async def _run(): async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac: response = await ac.post( "/api/wormhole/gate/message/decrypt", json={ "gate_id": gate_id, "epoch": composed["epoch"], "ciphertext": composed["ciphertext"], "nonce": composed["nonce"], "sender_ref": composed["sender_ref"], "format": "g1", }, headers={"X-Admin-Key": auth._current_admin_key()}, ) return response.json() try: result = asyncio.run(_run()) finally: gate_mls_mod.reset_gate_mls_state() assert composed["ok"] is True assert gate_mls_mod.is_gate_locked_to_mls(gate_id) is True assert result == { "ok": False, "detail": "gate is locked to MLS format", "gate_id": gate_id, "required_format": "mls1", "current_format": "g1", }