mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-07-12 15:06:37 +02:00
844 lines
34 KiB
Python
844 lines
34 KiB
Python
import base64
|
|
import asyncio
|
|
import json
|
|
import time
|
|
|
|
import pytest
|
|
from cryptography.hazmat.primitives.asymmetric import x25519
|
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
from cryptography.hazmat.primitives.serialization import Encoding, NoEncryption, PrivateFormat, PublicFormat
|
|
from starlette.requests import Request
|
|
|
|
|
|
def _fresh_mesh_state(tmp_path, monkeypatch):
|
|
from services.mesh import (
|
|
mesh_dm_relay,
|
|
mesh_wormhole_contacts,
|
|
mesh_secure_storage,
|
|
mesh_wormhole_identity,
|
|
mesh_wormhole_persona,
|
|
mesh_wormhole_root_manifest,
|
|
mesh_wormhole_root_transparency,
|
|
)
|
|
|
|
monkeypatch.setattr(mesh_dm_relay, "DATA_DIR", tmp_path)
|
|
monkeypatch.setattr(mesh_dm_relay, "RELAY_FILE", tmp_path / "dm_relay.json")
|
|
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",
|
|
)
|
|
monkeypatch.setattr(mesh_wormhole_root_manifest, "DATA_DIR", tmp_path)
|
|
monkeypatch.setattr(mesh_wormhole_root_transparency, "DATA_DIR", tmp_path)
|
|
monkeypatch.setattr(mesh_wormhole_contacts, "DATA_DIR", tmp_path)
|
|
monkeypatch.setattr(mesh_wormhole_contacts, "CONTACTS_FILE", tmp_path / "wormhole_dm_contacts.json")
|
|
relay = mesh_dm_relay.DMRelay()
|
|
monkeypatch.setattr(mesh_dm_relay, "dm_relay", relay)
|
|
return relay, mesh_wormhole_identity
|
|
|
|
|
|
def _json_request(path: str, body: dict) -> Request:
|
|
payload = json.dumps(body).encode("utf-8")
|
|
sent = {"value": False}
|
|
|
|
async def receive():
|
|
if sent["value"]:
|
|
return {"type": "http.request", "body": b"", "more_body": False}
|
|
sent["value"] = True
|
|
return {"type": "http.request", "body": payload, "more_body": False}
|
|
|
|
return Request(
|
|
{
|
|
"type": "http",
|
|
"headers": [(b"content-type", b"application/json")],
|
|
"client": ("test", 12345),
|
|
"method": "POST",
|
|
"path": path,
|
|
},
|
|
receive,
|
|
)
|
|
|
|
|
|
def test_sender_token_can_resolve_recipient_without_clear_recipient_id(tmp_path, monkeypatch):
|
|
_relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity_mod.bootstrap_wormhole_identity(force=True)
|
|
|
|
from services.mesh.mesh_wormhole_sender_token import (
|
|
consume_wormhole_dm_sender_token,
|
|
issue_wormhole_dm_sender_token,
|
|
)
|
|
|
|
issued = issue_wormhole_dm_sender_token(
|
|
recipient_id="peer123",
|
|
delivery_class="shared",
|
|
recipient_token="tok123",
|
|
)
|
|
assert issued["ok"]
|
|
|
|
consumed = consume_wormhole_dm_sender_token(
|
|
sender_token=issued["sender_token"],
|
|
recipient_id="",
|
|
delivery_class="shared",
|
|
recipient_token="tok123",
|
|
)
|
|
assert consumed["ok"]
|
|
assert consumed["recipient_id"] == "peer123"
|
|
assert consumed["sender_token_hash"]
|
|
|
|
|
|
def test_signed_prekey_rotation_preserves_old_bootstrap_decrypt(tmp_path, monkeypatch):
|
|
_relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity_mod.bootstrap_wormhole_identity(force=True)
|
|
monkeypatch.setenv("MESH_BLOCK_LEGACY_AGENT_ID_LOOKUP", "false")
|
|
|
|
from services.mesh.mesh_wormhole_contacts import (
|
|
confirm_sas_verification,
|
|
observe_remote_prekey_identity,
|
|
)
|
|
from services.config import get_settings
|
|
from services.mesh.mesh_wormhole_prekey import (
|
|
SIGNED_PREKEY_ROTATE_AFTER_S,
|
|
bootstrap_decrypt_from_sender,
|
|
bootstrap_encrypt_for_peer,
|
|
fetch_dm_prekey_bundle,
|
|
register_wormhole_prekey_bundle,
|
|
)
|
|
|
|
get_settings.cache_clear()
|
|
try:
|
|
reg1 = register_wormhole_prekey_bundle(force_signed_prekey=True)
|
|
assert reg1["ok"]
|
|
agent_id = reg1["agent_id"]
|
|
fetched = fetch_dm_prekey_bundle(agent_id)
|
|
observe_remote_prekey_identity(agent_id, fingerprint=str(fetched.get("trust_fingerprint", "") or ""))
|
|
monkeypatch.setattr(
|
|
"services.mesh.mesh_wormhole_contacts._derive_expected_contact_sas_phrase",
|
|
lambda *_args, **_kwargs: {"ok": True, "phrase": "able acid", "peer_ref": agent_id, "words": 2},
|
|
)
|
|
confirm_sas_verification(agent_id, "able acid")
|
|
|
|
old_envelope = bootstrap_encrypt_for_peer(agent_id, "ACCESS_REQUEST:X25519:testpub|geo=1,2")
|
|
assert old_envelope["ok"]
|
|
|
|
data = identity_mod.read_wormhole_identity()
|
|
data["signed_prekey_generated_at"] = int(time.time()) - SIGNED_PREKEY_ROTATE_AFTER_S - 10
|
|
identity_mod._write_identity(data)
|
|
|
|
reg2 = register_wormhole_prekey_bundle()
|
|
assert reg2["ok"]
|
|
assert reg2["bundle"]["signed_prekey_id"] != reg1["bundle"]["signed_prekey_id"]
|
|
|
|
refreshed = identity_mod.read_wormhole_identity()
|
|
history = list(refreshed.get("signed_prekey_history") or [])
|
|
assert any(int(item.get("signed_prekey_id", 0) or 0) == reg1["bundle"]["signed_prekey_id"] for item in history)
|
|
|
|
dec = bootstrap_decrypt_from_sender(agent_id, old_envelope["result"])
|
|
assert dec["ok"]
|
|
assert dec["result"] == "ACCESS_REQUEST:X25519:testpub|geo=1,2"
|
|
finally:
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def test_prekey_bundle_fetch_rejects_stale_or_tampered_bundle(tmp_path, monkeypatch):
|
|
relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity_mod.bootstrap_wormhole_identity(force=True)
|
|
monkeypatch.setenv("MESH_BLOCK_LEGACY_AGENT_ID_LOOKUP", "false")
|
|
|
|
from services.config import get_settings
|
|
from services.mesh import mesh_wormhole_prekey as prekey_mod
|
|
|
|
get_settings.cache_clear()
|
|
try:
|
|
registered = prekey_mod.register_wormhole_prekey_bundle(force_signed_prekey=True)
|
|
assert registered["ok"] is True
|
|
agent_id = registered["agent_id"]
|
|
|
|
fresh = prekey_mod.fetch_dm_prekey_bundle(agent_id)
|
|
assert fresh["ok"] is True
|
|
assert int(fresh["signed_at"]) > 0
|
|
assert fresh["bundle_signature"]
|
|
assert fresh["root_attestation"]["root_node_id"]
|
|
assert fresh["root_attestation"]["root_public_key"]
|
|
assert fresh["root_attestation"]["root_manifest_fingerprint"]
|
|
assert fresh["root_manifest"]["payload"]["root_fingerprint"]
|
|
assert fresh["root_manifest_witness"]["payload"]["manifest_fingerprint"]
|
|
assert len(fresh["root_manifest_witnesses"]) == 3
|
|
|
|
stored = relay.get_prekey_bundle(agent_id)
|
|
stale_bundle = dict(stored.get("bundle") or {})
|
|
stale_bundle["signed_at"] = int(time.time()) - prekey_mod._max_prekey_bundle_age_s() - 10
|
|
stale_bundle = prekey_mod._attach_bundle_signature(stale_bundle, signed_at=stale_bundle["signed_at"])
|
|
relay._prekey_bundles[agent_id]["bundle"] = stale_bundle
|
|
|
|
stale = prekey_mod.fetch_dm_prekey_bundle(agent_id)
|
|
assert stale == {"ok": False, "detail": "Prekey bundle is stale"}
|
|
|
|
tampered_bundle = dict(stale_bundle)
|
|
tampered_bundle["signed_at"] = int(time.time())
|
|
tampered_bundle = prekey_mod._attach_bundle_signature(tampered_bundle, signed_at=tampered_bundle["signed_at"])
|
|
tampered_bundle["bundle_signature"] = "00" * 64
|
|
relay._prekey_bundles[agent_id]["bundle"] = tampered_bundle
|
|
|
|
tampered = prekey_mod.fetch_dm_prekey_bundle(agent_id)
|
|
assert tampered == {"ok": False, "detail": "Prekey bundle signature invalid"}
|
|
|
|
root_tampered_bundle = dict(stored.get("bundle") or {})
|
|
root_tampered_attestation = dict(root_tampered_bundle.get("root_attestation") or {})
|
|
root_tampered_attestation["signature"] = "00" * 64
|
|
root_tampered_bundle["root_attestation"] = root_tampered_attestation
|
|
relay._prekey_bundles[agent_id]["bundle"] = root_tampered_bundle
|
|
|
|
root_tampered = prekey_mod.fetch_dm_prekey_bundle(agent_id)
|
|
assert root_tampered == {"ok": False, "detail": "Prekey bundle signature invalid"}
|
|
|
|
missing_root_bundle = dict(stored.get("bundle") or {})
|
|
missing_root_bundle.pop("root_attestation", None)
|
|
relay._prekey_bundles[agent_id]["bundle"] = missing_root_bundle
|
|
|
|
missing_root = prekey_mod.fetch_dm_prekey_bundle(agent_id)
|
|
assert missing_root == {"ok": False, "detail": "prekey bundle root attestation required"}
|
|
|
|
missing_manifest_bundle = dict(stored.get("bundle") or {})
|
|
missing_manifest_bundle.pop("root_manifest", None)
|
|
relay._prekey_bundles[agent_id]["bundle"] = missing_manifest_bundle
|
|
|
|
missing_manifest = prekey_mod.fetch_dm_prekey_bundle(agent_id)
|
|
assert missing_manifest == {"ok": False, "detail": "prekey bundle root manifest required"}
|
|
|
|
missing_transparency_bundle = dict(stored.get("bundle") or {})
|
|
missing_transparency_bundle.pop("root_transparency_record", None)
|
|
relay._prekey_bundles[agent_id]["bundle"] = missing_transparency_bundle
|
|
|
|
missing_transparency = prekey_mod.fetch_dm_prekey_bundle(agent_id)
|
|
assert missing_transparency == {"ok": False, "detail": "prekey bundle root transparency record required"}
|
|
finally:
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def test_prekey_bundle_fetch_rejects_unproven_witnessed_root_rotation(tmp_path, monkeypatch):
|
|
relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity_mod.bootstrap_wormhole_identity(force=True)
|
|
monkeypatch.setenv("MESH_BLOCK_LEGACY_AGENT_ID_LOOKUP", "false")
|
|
|
|
from services.config import get_settings
|
|
from services.mesh import mesh_wormhole_persona as persona_mod
|
|
from services.mesh import mesh_wormhole_prekey as prekey_mod
|
|
from services.mesh import mesh_wormhole_root_manifest as manifest_mod
|
|
from services.mesh import mesh_wormhole_root_transparency as transparency_mod
|
|
|
|
get_settings.cache_clear()
|
|
try:
|
|
registered = prekey_mod.register_wormhole_prekey_bundle(force_signed_prekey=True)
|
|
assert registered["ok"] is True
|
|
agent_id = registered["agent_id"]
|
|
|
|
persona_mod.bootstrap_wormhole_persona_state(force=True)
|
|
rotated = prekey_mod.register_wormhole_prekey_bundle(force_signed_prekey=True)
|
|
assert rotated["ok"] is True
|
|
|
|
tampered_manifest_payload = {
|
|
**dict((rotated.get("bundle") or {}).get("root_manifest", {}).get("payload") or {}),
|
|
"previous_root_cross_sequence": 0,
|
|
"previous_root_cross_signature": "",
|
|
}
|
|
resigned_manifest = persona_mod.sign_root_wormhole_event(
|
|
event_type=manifest_mod.STABLE_DM_ROOT_MANIFEST_EVENT_TYPE,
|
|
payload=tampered_manifest_payload,
|
|
)
|
|
tampered_manifest = {
|
|
"type": manifest_mod.STABLE_DM_ROOT_MANIFEST_TYPE,
|
|
"event_type": manifest_mod.STABLE_DM_ROOT_MANIFEST_EVENT_TYPE,
|
|
"node_id": str(resigned_manifest.get("node_id", "") or ""),
|
|
"public_key": str(resigned_manifest.get("public_key", "") or ""),
|
|
"public_key_algo": str(resigned_manifest.get("public_key_algo", "Ed25519") or "Ed25519"),
|
|
"protocol_version": str(resigned_manifest.get("protocol_version", "") or ""),
|
|
"sequence": int(resigned_manifest.get("sequence", 0) or 0),
|
|
"payload": dict(resigned_manifest.get("payload") or {}),
|
|
"signature": str(resigned_manifest.get("signature", "") or ""),
|
|
"identity_scope": "root",
|
|
}
|
|
witness_state = manifest_mod.read_root_distribution_state()
|
|
witness_identities = list(witness_state.get("witness_identities") or [])
|
|
tampered_witnesses = [
|
|
manifest_mod._sign_with_witness_identity(
|
|
identity=dict(identity or {}),
|
|
event_type=manifest_mod.STABLE_DM_ROOT_MANIFEST_WITNESS_EVENT_TYPE,
|
|
payload=manifest_mod._witness_payload(tampered_manifest),
|
|
)
|
|
for identity in witness_identities
|
|
]
|
|
tampered_transparency = transparency_mod.publish_root_transparency_record(
|
|
distribution={"manifest": tampered_manifest, "witnesses": tampered_witnesses}
|
|
)
|
|
result = prekey_mod._verify_bundle_root_distribution_impl(
|
|
{
|
|
"agent_id": agent_id,
|
|
"bundle": {
|
|
"root_manifest": tampered_manifest,
|
|
"root_manifest_witness": dict(tampered_witnesses[0] or {}),
|
|
"root_manifest_witnesses": tampered_witnesses,
|
|
"root_transparency_record": dict(tampered_transparency.get("record") or {}),
|
|
},
|
|
}
|
|
)
|
|
|
|
assert result[0] is False
|
|
assert result[1] == "prekey bundle root rotation proof required"
|
|
finally:
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def test_prekey_bundle_fetch_requires_witness_threshold(tmp_path, monkeypatch):
|
|
relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity_mod.bootstrap_wormhole_identity(force=True)
|
|
monkeypatch.setenv("MESH_BLOCK_LEGACY_AGENT_ID_LOOKUP", "false")
|
|
|
|
from services.config import get_settings
|
|
from services.mesh import mesh_wormhole_prekey as prekey_mod
|
|
|
|
get_settings.cache_clear()
|
|
try:
|
|
registered = prekey_mod.register_wormhole_prekey_bundle(force_signed_prekey=True)
|
|
assert registered["ok"] is True
|
|
agent_id = registered["agent_id"]
|
|
|
|
stored = relay.get_prekey_bundle(agent_id)
|
|
under_witnessed_bundle = dict(stored.get("bundle") or {})
|
|
under_witnessed_bundle["root_manifest_witnesses"] = [
|
|
dict(under_witnessed_bundle.get("root_manifest_witnesses", [])[0] or {})
|
|
]
|
|
relay._prekey_bundles[agent_id]["bundle"] = under_witnessed_bundle
|
|
|
|
rejected = prekey_mod.fetch_dm_prekey_bundle(agent_id)
|
|
|
|
assert rejected["ok"] is False
|
|
assert rejected["detail"] == "stable root manifest witness threshold not met"
|
|
finally:
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def test_prekey_bundle_fetch_requires_witness_policy_change_proof(tmp_path, monkeypatch):
|
|
relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity_mod.bootstrap_wormhole_identity(force=True)
|
|
monkeypatch.setenv("MESH_BLOCK_LEGACY_AGENT_ID_LOOKUP", "false")
|
|
|
|
from services.config import get_settings
|
|
from services.mesh import mesh_wormhole_persona as persona_mod
|
|
from services.mesh import mesh_wormhole_prekey as prekey_mod
|
|
from services.mesh import mesh_wormhole_root_manifest as manifest_mod
|
|
from services.mesh import mesh_wormhole_root_transparency as transparency_mod
|
|
|
|
get_settings.cache_clear()
|
|
try:
|
|
registered = prekey_mod.register_wormhole_prekey_bundle(force_signed_prekey=True)
|
|
assert registered["ok"] is True
|
|
agent_id = registered["agent_id"]
|
|
|
|
republished = manifest_mod.publish_current_root_manifest(expires_in_s=3600, policy_version=2)
|
|
|
|
tampered_manifest_payload = {
|
|
**dict((republished.get("manifest") or {}).get("payload") or {}),
|
|
"previous_witness_policy_sequence": 0,
|
|
"previous_witness_policy_signature": "",
|
|
}
|
|
resigned_manifest = persona_mod.sign_root_wormhole_event(
|
|
event_type=manifest_mod.STABLE_DM_ROOT_MANIFEST_EVENT_TYPE,
|
|
payload=tampered_manifest_payload,
|
|
)
|
|
tampered_manifest = {
|
|
"type": manifest_mod.STABLE_DM_ROOT_MANIFEST_TYPE,
|
|
"event_type": manifest_mod.STABLE_DM_ROOT_MANIFEST_EVENT_TYPE,
|
|
"node_id": str(resigned_manifest.get("node_id", "") or ""),
|
|
"public_key": str(resigned_manifest.get("public_key", "") or ""),
|
|
"public_key_algo": str(resigned_manifest.get("public_key_algo", "Ed25519") or "Ed25519"),
|
|
"protocol_version": str(resigned_manifest.get("protocol_version", "") or ""),
|
|
"sequence": int(resigned_manifest.get("sequence", 0) or 0),
|
|
"payload": dict(resigned_manifest.get("payload") or {}),
|
|
"signature": str(resigned_manifest.get("signature", "") or ""),
|
|
"identity_scope": "root",
|
|
}
|
|
witness_state = manifest_mod.read_root_distribution_state()
|
|
witness_identities = list(witness_state.get("witness_identities") or [])
|
|
tampered_witnesses = [
|
|
manifest_mod._sign_with_witness_identity(
|
|
identity=dict(identity or {}),
|
|
event_type=manifest_mod.STABLE_DM_ROOT_MANIFEST_WITNESS_EVENT_TYPE,
|
|
payload=manifest_mod._witness_payload(tampered_manifest),
|
|
)
|
|
for identity in witness_identities
|
|
]
|
|
tampered_transparency = transparency_mod.publish_root_transparency_record(
|
|
distribution={"manifest": tampered_manifest, "witnesses": tampered_witnesses}
|
|
)
|
|
result = prekey_mod._verify_bundle_root_distribution_impl(
|
|
{
|
|
"agent_id": agent_id,
|
|
"bundle": {
|
|
"root_manifest": tampered_manifest,
|
|
"root_manifest_witness": dict(tampered_witnesses[0] or {}),
|
|
"root_manifest_witnesses": tampered_witnesses,
|
|
"root_transparency_record": dict(tampered_transparency.get("record") or {}),
|
|
},
|
|
}
|
|
)
|
|
|
|
assert result[0] is False
|
|
assert result[1] == "prekey bundle root witness policy change proof required"
|
|
finally:
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def test_prekey_bundle_fetch_rejects_future_dated_bundle(tmp_path, monkeypatch):
|
|
relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity_mod.bootstrap_wormhole_identity(force=True)
|
|
monkeypatch.setenv("MESH_BLOCK_LEGACY_AGENT_ID_LOOKUP", "false")
|
|
|
|
from services.config import get_settings
|
|
from services.mesh import mesh_wormhole_prekey as prekey_mod
|
|
|
|
get_settings.cache_clear()
|
|
try:
|
|
registered = prekey_mod.register_wormhole_prekey_bundle(force_signed_prekey=True)
|
|
assert registered["ok"] is True
|
|
agent_id = registered["agent_id"]
|
|
|
|
stored = relay.get_prekey_bundle(agent_id)
|
|
future_bundle = dict(stored.get("bundle") or {})
|
|
future_bundle["signed_at"] = int(time.time()) + 301
|
|
future_bundle = prekey_mod._attach_bundle_signature(future_bundle, signed_at=future_bundle["signed_at"])
|
|
relay._prekey_bundles[agent_id]["bundle"] = future_bundle
|
|
|
|
future = prekey_mod.fetch_dm_prekey_bundle(agent_id)
|
|
assert future == {"ok": False, "detail": "Prekey bundle signed_at is in the future"}
|
|
finally:
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def test_remote_prekey_identity_is_pinned_and_detects_mismatch(tmp_path, monkeypatch):
|
|
_relay, _identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
from services.mesh import mesh_wormhole_contacts
|
|
|
|
monkeypatch.setattr(mesh_wormhole_contacts, "DATA_DIR", tmp_path)
|
|
monkeypatch.setattr(mesh_wormhole_contacts, "CONTACTS_FILE", tmp_path / "wormhole_dm_contacts.json")
|
|
|
|
pinned = mesh_wormhole_contacts.observe_remote_prekey_identity(
|
|
"peer-alpha",
|
|
fingerprint="aa" * 32,
|
|
sequence=3,
|
|
signed_at=111,
|
|
)
|
|
same = mesh_wormhole_contacts.observe_remote_prekey_identity(
|
|
"peer-alpha",
|
|
fingerprint="aa" * 32,
|
|
sequence=4,
|
|
signed_at=222,
|
|
)
|
|
changed = mesh_wormhole_contacts.observe_remote_prekey_identity(
|
|
"peer-alpha",
|
|
fingerprint="bb" * 32,
|
|
sequence=5,
|
|
signed_at=333,
|
|
)
|
|
|
|
assert pinned["trust_changed"] is False
|
|
assert same["trust_changed"] is False
|
|
assert changed["trust_changed"] is True
|
|
stored = mesh_wormhole_contacts.list_wormhole_dm_contacts()["peer-alpha"]
|
|
assert stored["remotePrekeyFingerprint"] == "aa" * 32
|
|
assert stored["remotePrekeyObservedFingerprint"] == "bb" * 32
|
|
assert stored["remotePrekeyMismatch"] is True
|
|
|
|
|
|
def test_compose_wormhole_dm_rejects_remote_prekey_identity_change(tmp_path, monkeypatch):
|
|
_relay, _identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
import main
|
|
from services.mesh import mesh_wormhole_contacts
|
|
|
|
monkeypatch.setattr(main, "has_mls_dm_session", lambda *_args, **_kwargs: {"ok": True, "exists": False})
|
|
monkeypatch.setattr(main, "initiate_mls_dm_session", lambda *_args, **_kwargs: {"ok": True, "welcome": "welcome"})
|
|
monkeypatch.setattr(main, "encrypt_mls_dm", lambda *_args, **_kwargs: {"ok": True, "ciphertext": "ct", "nonce": "n"})
|
|
|
|
initial = {
|
|
"ok": True,
|
|
"agent_id": "peer-alpha",
|
|
"mls_key_package": "ZmFrZQ==",
|
|
"identity_dh_pub_key": "peer-dh-pub",
|
|
"public_key": "peer-signing-pub",
|
|
"public_key_algo": "Ed25519",
|
|
"protocol_version": "infonet/2",
|
|
"sequence": 2,
|
|
"signed_at": int(time.time()),
|
|
"trust_fingerprint": "11" * 32,
|
|
}
|
|
changed = {
|
|
**initial,
|
|
"sequence": 3,
|
|
"signed_at": int(time.time()) + 1,
|
|
"trust_fingerprint": "22" * 32,
|
|
}
|
|
mesh_wormhole_contacts.observe_remote_prekey_identity("peer-alpha", fingerprint=initial["trust_fingerprint"])
|
|
monkeypatch.setattr(
|
|
mesh_wormhole_contacts,
|
|
"_derive_expected_contact_sas_phrase",
|
|
lambda *_args, **_kwargs: {"ok": True, "phrase": "able acid", "peer_ref": "peer-alpha", "words": 2},
|
|
)
|
|
mesh_wormhole_contacts.confirm_sas_verification("peer-alpha", "able acid")
|
|
|
|
first = main.compose_wormhole_dm(
|
|
peer_id="peer-alpha",
|
|
peer_dh_pub="peer-dh-pub",
|
|
plaintext="hello",
|
|
remote_prekey_bundle=initial,
|
|
)
|
|
second = main.compose_wormhole_dm(
|
|
peer_id="peer-alpha",
|
|
peer_dh_pub="peer-dh-pub",
|
|
plaintext="hello again",
|
|
remote_prekey_bundle=changed,
|
|
)
|
|
|
|
assert first["ok"] is True
|
|
assert second["ok"] is False
|
|
assert second["peer_id"] == "peer-alpha"
|
|
assert second["detail"] == "remote prekey identity changed; verification required"
|
|
assert second["trust_changed"] is True
|
|
assert second["trust_level"] == "continuity_broken"
|
|
|
|
|
|
def test_prekey_bundle_registration_rejects_invalid_bundle(tmp_path, monkeypatch):
|
|
relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity = identity_mod.bootstrap_wormhole_identity(force=True)
|
|
|
|
from services.mesh import mesh_wormhole_prekey as prekey_mod
|
|
|
|
bundle = prekey_mod.ensure_wormhole_prekeys(force_signed_prekey=True)
|
|
bundle = prekey_mod._attach_bundle_signature(bundle, signed_at=int(time.time()) + 301)
|
|
|
|
ok, reason, meta = relay.register_prekey_bundle(
|
|
identity["node_id"],
|
|
bundle,
|
|
"sig",
|
|
identity["public_key"],
|
|
identity["public_key_algo"],
|
|
"infonet/2",
|
|
1,
|
|
)
|
|
|
|
assert ok is False
|
|
assert reason == "Prekey bundle signed_at is in the future"
|
|
assert meta is None
|
|
assert relay.get_prekey_bundle(identity["node_id"]) is None
|
|
|
|
|
|
def test_dm_mailbox_token_derivation_and_shared_sender_token_routing(tmp_path, monkeypatch):
|
|
relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity = identity_mod.bootstrap_wormhole_identity(force=True)
|
|
|
|
from services.mesh.mesh_wormhole_identity import derive_dm_mailbox_token
|
|
from services.mesh.mesh_wormhole_sender_token import issue_wormhole_dm_sender_token
|
|
import main
|
|
from services import wormhole_supervisor
|
|
from services.mesh import mesh_dm_relay, mesh_hashchain
|
|
|
|
mailbox_token = derive_dm_mailbox_token(identity["node_id"])
|
|
assert mailbox_token
|
|
|
|
issued = issue_wormhole_dm_sender_token(
|
|
recipient_id="peer123",
|
|
delivery_class="shared",
|
|
recipient_token=mailbox_token,
|
|
)
|
|
assert issued["ok"] is True
|
|
|
|
monkeypatch.setattr(main, "_verify_signed_write", lambda **_kwargs: (True, ""))
|
|
monkeypatch.setattr(main, "_secure_dm_enabled", lambda: False)
|
|
monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_strong")
|
|
monkeypatch.setattr(mesh_hashchain.infonet, "validate_and_set_sequence", lambda *_args, **_kwargs: (True, ""))
|
|
monkeypatch.setattr(mesh_dm_relay, "dm_relay", relay)
|
|
|
|
response = asyncio.run(
|
|
main.dm_send(
|
|
_json_request(
|
|
"/api/mesh/dm/send",
|
|
{
|
|
"sender_token": issued["sender_token"],
|
|
"recipient_id": "",
|
|
"delivery_class": "shared",
|
|
"recipient_token": mailbox_token,
|
|
"ciphertext": "cipher-shared",
|
|
"sender_seal": "v3:test-seal",
|
|
"msg_id": "shared-msg-1",
|
|
"timestamp": int(time.time()),
|
|
"public_key": "",
|
|
"public_key_algo": "Ed25519",
|
|
"signature": "sig",
|
|
"sequence": 1,
|
|
"protocol_version": "infonet/2",
|
|
},
|
|
)
|
|
)
|
|
)
|
|
|
|
assert response["ok"] is True
|
|
hashed_mailbox = relay._hashed_mailbox_token(mailbox_token)
|
|
assert list(relay._mailboxes.keys()) == [hashed_mailbox]
|
|
assert relay._mailboxes[hashed_mailbox][0].sender_id.startswith("sender_token:")
|
|
assert relay._mailboxes[hashed_mailbox][0].sender_id != identity["node_id"]
|
|
delivered, _ = relay.collect_claims(identity["node_id"], [{"type": "shared", "token": mailbox_token}])
|
|
assert [msg["msg_id"] for msg in delivered] == ["shared-msg-1"]
|
|
|
|
|
|
def test_open_sender_seal_verifies_in_wormhole(tmp_path, monkeypatch):
|
|
_relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity = identity_mod.bootstrap_wormhole_identity(force=True)
|
|
|
|
from services.mesh.mesh_wormhole_seal import build_sender_seal, open_sender_seal
|
|
|
|
msg_id = "dm_test_1"
|
|
timestamp = 1234567890
|
|
built = build_sender_seal(
|
|
recipient_id=identity["node_id"],
|
|
recipient_dh_pub=identity["dh_pub_key"],
|
|
msg_id=msg_id,
|
|
timestamp=timestamp,
|
|
)
|
|
assert built["ok"]
|
|
assert str(built["sender_seal"]).startswith("v3:")
|
|
|
|
opened = open_sender_seal(
|
|
sender_seal=built["sender_seal"],
|
|
candidate_dh_pub=identity["dh_pub_key"],
|
|
recipient_id=identity["node_id"],
|
|
expected_msg_id=msg_id,
|
|
)
|
|
assert opened["ok"]
|
|
assert opened["sender_id"] == identity["node_id"]
|
|
assert opened["seal_verified"] is True
|
|
|
|
|
|
def test_build_sender_seal_uses_saved_contact_dh_key(tmp_path, monkeypatch):
|
|
_relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity = identity_mod.bootstrap_wormhole_identity(force=True)
|
|
|
|
from services.mesh import mesh_wormhole_contacts
|
|
from services.mesh.mesh_wormhole_seal import build_sender_seal
|
|
|
|
monkeypatch.setattr(mesh_wormhole_contacts, "DATA_DIR", tmp_path)
|
|
monkeypatch.setattr(mesh_wormhole_contacts, "CONTACTS_FILE", tmp_path / "wormhole_dm_contacts.json")
|
|
mesh_wormhole_contacts.upsert_wormhole_dm_contact(
|
|
identity["node_id"],
|
|
{
|
|
"sharedAlias": "dmx_self",
|
|
"dhPubKey": identity["dh_pub_key"],
|
|
},
|
|
)
|
|
|
|
built = build_sender_seal(
|
|
recipient_id=identity["node_id"],
|
|
recipient_dh_pub="",
|
|
msg_id="dm_test_saved_contact_key",
|
|
timestamp=1234567890,
|
|
)
|
|
|
|
assert built["ok"] is True
|
|
assert str(built["sender_seal"]).startswith("v3:")
|
|
|
|
|
|
def test_router_dm_helper_request_models_allow_inferred_peer_material():
|
|
from routers import wormhole
|
|
|
|
open_req = wormhole.WormholeOpenSealRequest(
|
|
sender_seal="v3:test",
|
|
recipient_id="peer-open",
|
|
expected_msg_id="msg-open",
|
|
)
|
|
build_req = wormhole.WormholeBuildSealRequest(
|
|
recipient_id="peer-build",
|
|
msg_id="msg-build",
|
|
timestamp=123,
|
|
)
|
|
dead_drop_req = wormhole.WormholeDeadDropTokenRequest(peer_id="peer-dead-drop")
|
|
sas_req = wormhole.WormholeSasRequest(peer_id="peer-sas")
|
|
|
|
assert open_req.candidate_dh_pub == ""
|
|
assert build_req.recipient_dh_pub == ""
|
|
assert dead_drop_req.peer_dh_pub == ""
|
|
assert sas_req.peer_dh_pub == ""
|
|
assert sas_req.peer_ref == ""
|
|
|
|
|
|
def test_build_sender_seal_uses_cached_prekey_bundle_when_contact_dh_missing(tmp_path, monkeypatch):
|
|
_relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity = identity_mod.bootstrap_wormhole_identity(force=True)
|
|
|
|
from services.mesh import mesh_wormhole_prekey
|
|
from services.mesh.mesh_wormhole_seal import build_sender_seal
|
|
|
|
monkeypatch.setattr(
|
|
mesh_wormhole_prekey,
|
|
"fetch_dm_prekey_bundle",
|
|
lambda *, agent_id="", lookup_token="": {
|
|
"ok": True,
|
|
"identity_dh_pub_key": identity["dh_pub_key"],
|
|
},
|
|
)
|
|
|
|
built = build_sender_seal(
|
|
recipient_id=identity["node_id"],
|
|
recipient_dh_pub="",
|
|
msg_id="dm_test_cached_prekey_key",
|
|
timestamp=1234567890,
|
|
)
|
|
|
|
assert built["ok"] is True
|
|
assert str(built["sender_seal"]).startswith("v3:")
|
|
|
|
|
|
def test_open_sender_seal_v3_does_not_require_candidate_dh_pub(tmp_path, monkeypatch):
|
|
_relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity = identity_mod.bootstrap_wormhole_identity(force=True)
|
|
|
|
from services.mesh.mesh_wormhole_seal import build_sender_seal, open_sender_seal
|
|
|
|
msg_id = "dm_test_v3_no_candidate"
|
|
built = build_sender_seal(
|
|
recipient_id=identity["node_id"],
|
|
recipient_dh_pub=identity["dh_pub_key"],
|
|
msg_id=msg_id,
|
|
timestamp=1234567890,
|
|
)
|
|
assert built["ok"] is True
|
|
|
|
opened = open_sender_seal(
|
|
sender_seal=built["sender_seal"],
|
|
candidate_dh_pub="",
|
|
recipient_id=identity["node_id"],
|
|
expected_msg_id=msg_id,
|
|
)
|
|
|
|
assert opened["ok"] is True
|
|
assert opened["sender_id"] == identity["node_id"]
|
|
assert opened["seal_verified"] is True
|
|
|
|
|
|
def test_open_sender_seal_still_accepts_legacy_format(tmp_path, monkeypatch):
|
|
_relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity = identity_mod.bootstrap_wormhole_identity(force=True)
|
|
|
|
from services.mesh.mesh_wormhole_identity import sign_wormhole_message
|
|
from services.mesh import mesh_wormhole_seal
|
|
from services.mesh.mesh_wormhole_seal import open_sender_seal
|
|
|
|
monkeypatch.setattr(
|
|
mesh_wormhole_seal,
|
|
"read_wormhole_settings",
|
|
lambda: {"enabled": False, "anonymous_mode": False},
|
|
)
|
|
|
|
sender_priv = x25519.X25519PrivateKey.generate()
|
|
sender_pub = sender_priv.public_key()
|
|
recipient_pub = x25519.X25519PublicKey.from_public_bytes(base64.b64decode(identity["dh_pub_key"]))
|
|
shared = sender_priv.exchange(recipient_pub)
|
|
|
|
msg_id = "dm_test_legacy"
|
|
timestamp = 1234567890
|
|
signed = sign_wormhole_message(f"seal|{msg_id}|{timestamp}|{identity['node_id']}")
|
|
seal_payload = {
|
|
"sender_id": signed["node_id"],
|
|
"public_key": signed["public_key"],
|
|
"public_key_algo": signed["public_key_algo"],
|
|
"msg_id": msg_id,
|
|
"timestamp": timestamp,
|
|
"signature": signed["signature"],
|
|
}
|
|
iv = b"\x00" * 12
|
|
ciphertext = AESGCM(shared).encrypt(iv, json.dumps(seal_payload).encode("utf-8"), None)
|
|
sender_seal = base64.b64encode(iv + ciphertext).decode("ascii")
|
|
candidate_dh_pub = base64.b64encode(
|
|
sender_pub.public_bytes(Encoding.Raw, PublicFormat.Raw)
|
|
).decode("ascii")
|
|
|
|
opened = open_sender_seal(
|
|
sender_seal=sender_seal,
|
|
candidate_dh_pub=candidate_dh_pub,
|
|
recipient_id=identity["node_id"],
|
|
expected_msg_id=msg_id,
|
|
)
|
|
assert opened["ok"]
|
|
assert opened["sender_id"] == identity["node_id"]
|
|
assert opened["seal_verified"] is True
|
|
|
|
|
|
def test_legacy_sender_seal_rejected_in_hardened_mode(tmp_path, monkeypatch):
|
|
_relay, identity_mod = _fresh_mesh_state(tmp_path, monkeypatch)
|
|
identity = identity_mod.bootstrap_wormhole_identity(force=True)
|
|
|
|
from services.mesh.mesh_wormhole_identity import sign_wormhole_message
|
|
from services.mesh import mesh_wormhole_seal
|
|
|
|
monkeypatch.setattr(
|
|
mesh_wormhole_seal,
|
|
"read_wormhole_settings",
|
|
lambda: {"enabled": True, "anonymous_mode": True},
|
|
)
|
|
|
|
sender_priv = x25519.X25519PrivateKey.generate()
|
|
sender_pub = sender_priv.public_key()
|
|
recipient_pub = x25519.X25519PublicKey.from_public_bytes(base64.b64decode(identity["dh_pub_key"]))
|
|
shared = sender_priv.exchange(recipient_pub)
|
|
|
|
msg_id = "dm_test_legacy_hardened"
|
|
timestamp = 1234567890
|
|
signed = sign_wormhole_message(f"seal|{msg_id}|{timestamp}|{identity['node_id']}")
|
|
seal_payload = {
|
|
"sender_id": signed["node_id"],
|
|
"public_key": signed["public_key"],
|
|
"public_key_algo": signed["public_key_algo"],
|
|
"msg_id": msg_id,
|
|
"timestamp": timestamp,
|
|
"signature": signed["signature"],
|
|
}
|
|
iv = b"\x00" * 12
|
|
ciphertext = AESGCM(shared).encrypt(iv, json.dumps(seal_payload).encode("utf-8"), None)
|
|
sender_seal = base64.b64encode(iv + ciphertext).decode("ascii")
|
|
candidate_dh_pub = base64.b64encode(
|
|
sender_pub.public_bytes(Encoding.Raw, PublicFormat.Raw)
|
|
).decode("ascii")
|
|
|
|
opened = mesh_wormhole_seal.open_sender_seal(
|
|
sender_seal=sender_seal,
|
|
candidate_dh_pub=candidate_dh_pub,
|
|
recipient_id=identity["node_id"],
|
|
expected_msg_id=msg_id,
|
|
)
|
|
assert opened["ok"] is False
|
|
assert "Legacy sender seals" in opened["detail"]
|
|
|
|
|
|
def test_require_admin_no_longer_trusts_loopback_without_override(monkeypatch):
|
|
from fastapi import HTTPException
|
|
from starlette.requests import Request
|
|
import main
|
|
import auth
|
|
|
|
monkeypatch.setattr(auth, "_current_admin_key", lambda: "")
|
|
monkeypatch.setattr(auth, "_allow_insecure_admin", lambda: False)
|
|
|
|
request = Request(
|
|
{
|
|
"type": "http",
|
|
"headers": [],
|
|
"client": ("127.0.0.1", 12345),
|
|
"method": "GET",
|
|
"path": "/api/wormhole/status",
|
|
}
|
|
)
|
|
|
|
with pytest.raises(HTTPException) as exc:
|
|
main.require_admin(request)
|
|
assert exc.value.status_code == 403
|