mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-05-08 02:16:41 +02:00
470 lines
17 KiB
Python
470 lines
17 KiB
Python
"""Mesh protocol helpers for canonical payloads and versioning."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
from typing import Any
|
|
PROTOCOL_VERSION = "infonet/2"
|
|
NETWORK_ID = "sb-testnet-0"
|
|
SIGNED_CONTEXT_PROTOCOL = "shadowbroker"
|
|
SIGNED_CONTEXT_VERSION = 1
|
|
SIGNED_CONTEXT_FIELD = "signed_context"
|
|
|
|
|
|
def _safe_int(val, default=0):
|
|
try:
|
|
return int(val)
|
|
except (TypeError, ValueError):
|
|
return default
|
|
|
|
|
|
def _normalize_signed_context(value: Any) -> dict[str, Any]:
|
|
if not isinstance(value, dict):
|
|
return {}
|
|
normalized = {
|
|
"protocol": str(value.get("protocol", "") or ""),
|
|
"version": _safe_int(value.get("version", 0), 0),
|
|
"event_type": str(value.get("event_type", "") or ""),
|
|
"kind": str(value.get("kind", "") or ""),
|
|
"endpoint": str(value.get("endpoint", "") or ""),
|
|
"lane_floor": str(value.get("lane_floor", "") or "").strip().lower(),
|
|
"sequence_domain": str(value.get("sequence_domain", "") or ""),
|
|
"node_id": str(value.get("node_id", "") or ""),
|
|
"sequence": _safe_int(value.get("sequence", 0), 0),
|
|
"body_hash": str(value.get("body_hash", "") or "").strip().lower(),
|
|
}
|
|
gate_id = str(value.get("gate_id", "") or "").strip().lower()
|
|
if gate_id:
|
|
normalized["gate_id"] = gate_id
|
|
recipient_id = str(value.get("recipient_id", "") or "").strip()
|
|
if recipient_id:
|
|
normalized["recipient_id"] = recipient_id
|
|
target_id = str(value.get("target_id", "") or "").strip()
|
|
if target_id:
|
|
normalized["target_id"] = target_id
|
|
return normalized
|
|
|
|
|
|
def _copy_signed_context(normalized: dict[str, Any], payload: dict[str, Any]) -> None:
|
|
signed_context = _normalize_signed_context(payload.get(SIGNED_CONTEXT_FIELD))
|
|
if signed_context:
|
|
normalized[SIGNED_CONTEXT_FIELD] = signed_context
|
|
|
|
|
|
def _normalize_number(value: Any) -> int | float:
|
|
try:
|
|
num = float(value)
|
|
except Exception:
|
|
return 0
|
|
if num.is_integer():
|
|
return int(num)
|
|
return num
|
|
|
|
|
|
def payload_body_hash(event_type: str, payload: dict[str, Any]) -> str:
|
|
"""Return a SHA-256 hash of the normalized payload without signed_context."""
|
|
from services.mesh.mesh_crypto import canonical_json
|
|
|
|
base_payload = dict(payload or {})
|
|
base_payload.pop(SIGNED_CONTEXT_FIELD, None)
|
|
normalized = normalize_payload(event_type, base_payload)
|
|
normalized.pop(SIGNED_CONTEXT_FIELD, None)
|
|
return hashlib.sha256(canonical_json(normalized).encode("utf-8")).hexdigest()
|
|
|
|
|
|
def build_signed_context(
|
|
*,
|
|
event_type: str,
|
|
kind: str,
|
|
endpoint: str,
|
|
lane_floor: str,
|
|
sequence_domain: str,
|
|
node_id: str,
|
|
sequence: int,
|
|
payload: dict[str, Any],
|
|
gate_id: str = "",
|
|
recipient_id: str = "",
|
|
target_id: str = "",
|
|
) -> dict[str, Any]:
|
|
context = {
|
|
"protocol": SIGNED_CONTEXT_PROTOCOL,
|
|
"version": SIGNED_CONTEXT_VERSION,
|
|
"event_type": str(event_type or ""),
|
|
"kind": str(kind or ""),
|
|
"endpoint": str(endpoint or ""),
|
|
"lane_floor": str(lane_floor or "").strip().lower(),
|
|
"sequence_domain": str(sequence_domain or ""),
|
|
"node_id": str(node_id or ""),
|
|
"sequence": _safe_int(sequence, 0),
|
|
"body_hash": payload_body_hash(event_type, payload),
|
|
}
|
|
if gate_id:
|
|
context["gate_id"] = str(gate_id).strip().lower()
|
|
if recipient_id:
|
|
context["recipient_id"] = str(recipient_id).strip()
|
|
if target_id:
|
|
context["target_id"] = str(target_id).strip()
|
|
return context
|
|
|
|
|
|
def validate_signed_context(
|
|
*,
|
|
event_type: str,
|
|
kind: str,
|
|
endpoint: str,
|
|
lane_floor: str,
|
|
sequence_domain: str,
|
|
node_id: str,
|
|
sequence: int,
|
|
payload: dict[str, Any],
|
|
gate_id: str = "",
|
|
recipient_id: str = "",
|
|
target_id: str = "",
|
|
) -> tuple[bool, str]:
|
|
supplied = _normalize_signed_context((payload or {}).get(SIGNED_CONTEXT_FIELD))
|
|
if not supplied:
|
|
return True, "signed_context_absent"
|
|
expected = build_signed_context(
|
|
event_type=event_type,
|
|
kind=kind,
|
|
endpoint=endpoint,
|
|
lane_floor=lane_floor,
|
|
sequence_domain=sequence_domain,
|
|
node_id=node_id,
|
|
sequence=sequence,
|
|
payload=payload,
|
|
gate_id=gate_id,
|
|
recipient_id=recipient_id,
|
|
target_id=target_id,
|
|
)
|
|
if supplied != expected:
|
|
return False, "signed_context_mismatch"
|
|
return True, "signed_context_ok"
|
|
|
|
|
|
def normalize_message_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
normalized = {
|
|
"message": str(payload.get("message", "")),
|
|
"destination": str(payload.get("destination", "")),
|
|
"channel": str(payload.get("channel", "LongFast")),
|
|
"priority": str(payload.get("priority", "normal")),
|
|
"ephemeral": bool(payload.get("ephemeral", False)),
|
|
}
|
|
transport_lock = str(payload.get("transport_lock", "") or "").strip().lower()
|
|
if transport_lock:
|
|
normalized["transport_lock"] = transport_lock
|
|
_copy_signed_context(normalized, payload)
|
|
return normalized
|
|
|
|
|
|
def normalize_gate_message_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
normalized = {
|
|
"gate": str(payload.get("gate", "")).strip().lower(),
|
|
"ciphertext": str(payload.get("ciphertext", "")),
|
|
"nonce": str(payload.get("nonce", payload.get("iv", ""))),
|
|
"sender_ref": str(payload.get("sender_ref", "")),
|
|
"format": str(payload.get("format", "mls1") or "mls1").strip().lower(),
|
|
}
|
|
epoch = _safe_int(payload.get("epoch", 0), 0)
|
|
if epoch > 0:
|
|
normalized["epoch"] = epoch
|
|
# gate_envelope carries cross-node decryptable ciphertext — preserve it
|
|
# on-chain so receiving nodes can decrypt without MLS key exchange.
|
|
gate_envelope = str(payload.get("gate_envelope", "") or "").strip()
|
|
if gate_envelope:
|
|
normalized["gate_envelope"] = gate_envelope
|
|
# envelope_hash binds gate_envelope to the signed payload (SHA-256 hex).
|
|
envelope_hash = str(payload.get("envelope_hash", "") or "").strip()
|
|
if envelope_hash:
|
|
normalized["envelope_hash"] = envelope_hash
|
|
# reply_to is a display-only parent message reference.
|
|
reply_to = str(payload.get("reply_to", "") or "").strip()
|
|
if reply_to:
|
|
normalized["reply_to"] = reply_to
|
|
transport_lock = str(payload.get("transport_lock", "") or "").strip().lower()
|
|
if transport_lock:
|
|
normalized["transport_lock"] = transport_lock
|
|
_copy_signed_context(normalized, payload)
|
|
return normalized
|
|
|
|
|
|
def normalize_vote_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
vote_val = _safe_int(payload.get("vote", 0), 0)
|
|
normalized = {
|
|
"target_id": str(payload.get("target_id", "")),
|
|
"vote": vote_val,
|
|
"gate": str(payload.get("gate", "")),
|
|
}
|
|
_copy_signed_context(normalized, payload)
|
|
return normalized
|
|
|
|
|
|
def normalize_gate_create_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
rules = payload.get("rules", {})
|
|
if not isinstance(rules, dict):
|
|
rules = {}
|
|
normalized = {
|
|
"gate_id": str(payload.get("gate_id", "")).lower(),
|
|
"display_name": str(payload.get("display_name", ""))[:64],
|
|
"rules": rules,
|
|
}
|
|
_copy_signed_context(normalized, payload)
|
|
return normalized
|
|
|
|
|
|
def normalize_prediction_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
return {
|
|
"market_title": str(payload.get("market_title", "")),
|
|
"side": str(payload.get("side", "")),
|
|
"stake_amount": _normalize_number(payload.get("stake_amount", 0.0)),
|
|
}
|
|
|
|
|
|
def normalize_stake_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
return {
|
|
"message_id": str(payload.get("message_id", "")),
|
|
"poster_id": str(payload.get("poster_id", "")),
|
|
"side": str(payload.get("side", "")),
|
|
"amount": _normalize_number(payload.get("amount", 0.0)),
|
|
"duration_days": _safe_int(payload.get("duration_days", 0), 0),
|
|
}
|
|
|
|
|
|
def normalize_dm_key_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
normalized = {
|
|
"dh_pub_key": str(payload.get("dh_pub_key", "")),
|
|
"dh_algo": str(payload.get("dh_algo", "")),
|
|
"timestamp": _safe_int(payload.get("timestamp", 0), 0),
|
|
}
|
|
transport_lock = str(payload.get("transport_lock", "") or "").strip().lower()
|
|
if transport_lock:
|
|
normalized["transport_lock"] = transport_lock
|
|
_copy_signed_context(normalized, payload)
|
|
return normalized
|
|
|
|
|
|
def normalize_dm_message_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
normalized = {
|
|
"recipient_id": str(payload.get("recipient_id", "")),
|
|
"delivery_class": str(payload.get("delivery_class", "")).lower(),
|
|
"recipient_token": str(payload.get("recipient_token", "")),
|
|
"ciphertext": str(payload.get("ciphertext", "")),
|
|
"msg_id": str(payload.get("msg_id", "")),
|
|
"timestamp": _safe_int(payload.get("timestamp", 0), 0),
|
|
"format": str(payload.get("format", "dm1") or "dm1").strip().lower(),
|
|
}
|
|
session_welcome = payload.get("session_welcome")
|
|
if session_welcome:
|
|
normalized["session_welcome"] = str(session_welcome)
|
|
sender_seal = str(payload.get("sender_seal", "") or "")
|
|
if sender_seal:
|
|
normalized["sender_seal"] = sender_seal
|
|
relay_salt = str(payload.get("relay_salt", "") or "").strip().lower()
|
|
if relay_salt:
|
|
normalized["relay_salt"] = relay_salt
|
|
transport_lock = str(payload.get("transport_lock", "") or "").strip().lower()
|
|
if transport_lock:
|
|
normalized["transport_lock"] = transport_lock
|
|
_copy_signed_context(normalized, payload)
|
|
return normalized
|
|
|
|
|
|
def normalize_dm_message_payload_legacy(payload: dict[str, Any]) -> dict[str, Any]:
|
|
return {
|
|
"recipient_id": str(payload.get("recipient_id", "")),
|
|
"delivery_class": str(payload.get("delivery_class", "")).lower(),
|
|
"recipient_token": str(payload.get("recipient_token", "")),
|
|
"ciphertext": str(payload.get("ciphertext", "")),
|
|
"msg_id": str(payload.get("msg_id", "")),
|
|
"timestamp": _safe_int(payload.get("timestamp", 0), 0),
|
|
}
|
|
|
|
|
|
def normalize_mailbox_claims(payload: dict[str, Any]) -> list[dict[str, str]]:
|
|
claims = payload.get("mailbox_claims", [])
|
|
if not isinstance(claims, list):
|
|
return []
|
|
normalized: list[dict[str, str]] = []
|
|
for claim in claims:
|
|
if not isinstance(claim, dict):
|
|
continue
|
|
normalized.append(
|
|
{
|
|
"type": str(claim.get("type", "")).lower(),
|
|
"token": str(claim.get("token", "")),
|
|
}
|
|
)
|
|
return normalized
|
|
|
|
|
|
def normalize_dm_poll_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
normalized = {
|
|
"mailbox_claims": normalize_mailbox_claims(payload),
|
|
"timestamp": _safe_int(payload.get("timestamp", 0), 0),
|
|
"nonce": str(payload.get("nonce", "")),
|
|
}
|
|
transport_lock = str(payload.get("transport_lock", "") or "").strip().lower()
|
|
if transport_lock:
|
|
normalized["transport_lock"] = transport_lock
|
|
_copy_signed_context(normalized, payload)
|
|
return normalized
|
|
|
|
|
|
def normalize_dm_count_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
return normalize_dm_poll_payload(payload)
|
|
|
|
|
|
def normalize_dm_block_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
normalized = {
|
|
"blocked_id": str(payload.get("blocked_id", "")),
|
|
"action": str(payload.get("action", "block")).lower(),
|
|
}
|
|
transport_lock = str(payload.get("transport_lock", "") or "").strip().lower()
|
|
if transport_lock:
|
|
normalized["transport_lock"] = transport_lock
|
|
_copy_signed_context(normalized, payload)
|
|
return normalized
|
|
|
|
|
|
def normalize_dm_key_witness_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
normalized = {
|
|
"target_id": str(payload.get("target_id", "")),
|
|
"dh_pub_key": str(payload.get("dh_pub_key", "")),
|
|
"timestamp": _safe_int(payload.get("timestamp", 0), 0),
|
|
}
|
|
transport_lock = str(payload.get("transport_lock", "") or "").strip().lower()
|
|
if transport_lock:
|
|
normalized["transport_lock"] = transport_lock
|
|
_copy_signed_context(normalized, payload)
|
|
return normalized
|
|
|
|
|
|
def normalize_trust_vouch_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
normalized = {
|
|
"target_id": str(payload.get("target_id", "")),
|
|
"note": str(payload.get("note", ""))[:140],
|
|
"timestamp": _safe_int(payload.get("timestamp", 0), 0),
|
|
}
|
|
transport_lock = str(payload.get("transport_lock", "") or "").strip().lower()
|
|
if transport_lock:
|
|
normalized["transport_lock"] = transport_lock
|
|
_copy_signed_context(normalized, payload)
|
|
return normalized
|
|
|
|
|
|
def normalize_key_rotate_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
normalized = {
|
|
"old_node_id": str(payload.get("old_node_id", "")),
|
|
"old_public_key": str(payload.get("old_public_key", "")),
|
|
"old_public_key_algo": str(payload.get("old_public_key_algo", "")),
|
|
"new_public_key": str(payload.get("new_public_key", "")),
|
|
"new_public_key_algo": str(payload.get("new_public_key_algo", "")),
|
|
"timestamp": _safe_int(payload.get("timestamp", 0), 0),
|
|
"old_signature": str(payload.get("old_signature", "")),
|
|
}
|
|
transport_lock = str(payload.get("transport_lock", "") or "").strip().lower()
|
|
if transport_lock:
|
|
normalized["transport_lock"] = transport_lock
|
|
_copy_signed_context(normalized, payload)
|
|
return normalized
|
|
|
|
|
|
def normalize_key_revoke_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
normalized = {
|
|
"revoked_public_key": str(payload.get("revoked_public_key", "")),
|
|
"revoked_public_key_algo": str(payload.get("revoked_public_key_algo", "")),
|
|
"revoked_at": _safe_int(payload.get("revoked_at", 0), 0),
|
|
"grace_until": _safe_int(payload.get("grace_until", 0), 0),
|
|
"reason": str(payload.get("reason", ""))[:140],
|
|
}
|
|
transport_lock = str(payload.get("transport_lock", "") or "").strip().lower()
|
|
if transport_lock:
|
|
normalized["transport_lock"] = transport_lock
|
|
_copy_signed_context(normalized, payload)
|
|
return normalized
|
|
|
|
|
|
def normalize_abuse_report_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
return {
|
|
"target_id": str(payload.get("target_id", "")),
|
|
"reason": str(payload.get("reason", ""))[:280],
|
|
"gate": str(payload.get("gate", "")),
|
|
"evidence": str(payload.get("evidence", ""))[:256],
|
|
}
|
|
|
|
|
|
def normalize_sar_anomaly_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
"""Canonical wire shape for a signed SAR anomaly event.
|
|
|
|
Mirrors ``services.sar.sar_signing.build_signed_payload`` exactly so the
|
|
verifier sees the same fields the signer signed. ``evidence_hash`` is
|
|
the load-bearing binding — it is computed over the canonical anomaly
|
|
JSON before signing and reproduced on the verifier side.
|
|
"""
|
|
def _f(name: str, default: float = 0.0) -> float:
|
|
try:
|
|
return float(payload.get(name, default) or 0.0)
|
|
except (TypeError, ValueError):
|
|
return default
|
|
|
|
def _i(name: str, default: int = 0) -> int:
|
|
try:
|
|
return int(payload.get(name, default) or 0)
|
|
except (TypeError, ValueError):
|
|
return default
|
|
|
|
return {
|
|
"anomaly_id": str(payload.get("anomaly_id", ""))[:128],
|
|
"kind": str(payload.get("kind", ""))[:48],
|
|
"lat": _f("lat"),
|
|
"lon": _f("lon"),
|
|
"magnitude": _f("magnitude"),
|
|
"magnitude_unit": str(payload.get("magnitude_unit", ""))[:32],
|
|
"confidence": _f("confidence"),
|
|
"first_seen": str(payload.get("first_seen", ""))[:32],
|
|
"last_seen": str(payload.get("last_seen", ""))[:32],
|
|
"stack_id": str(payload.get("stack_id", ""))[:64],
|
|
"scene_count": _i("scene_count"),
|
|
"evidence_hash": str(payload.get("evidence_hash", ""))[:128],
|
|
"solver": str(payload.get("solver", ""))[:64],
|
|
"source_constellation": str(payload.get("source_constellation", ""))[:64],
|
|
}
|
|
|
|
|
|
def normalize_payload(event_type: str, payload: dict[str, Any]) -> dict[str, Any]:
|
|
if event_type == "message":
|
|
return normalize_message_payload(payload)
|
|
if event_type == "gate_message":
|
|
return normalize_gate_message_payload(payload)
|
|
if event_type == "vote":
|
|
return normalize_vote_payload(payload)
|
|
if event_type == "gate_create":
|
|
return normalize_gate_create_payload(payload)
|
|
if event_type == "prediction":
|
|
return normalize_prediction_payload(payload)
|
|
if event_type == "stake":
|
|
return normalize_stake_payload(payload)
|
|
if event_type == "dm_key":
|
|
return normalize_dm_key_payload(payload)
|
|
if event_type == "dm_message":
|
|
return normalize_dm_message_payload(payload)
|
|
if event_type == "dm_poll":
|
|
return normalize_dm_poll_payload(payload)
|
|
if event_type == "dm_count":
|
|
return normalize_dm_count_payload(payload)
|
|
if event_type == "dm_block":
|
|
return normalize_dm_block_payload(payload)
|
|
if event_type == "dm_key_witness":
|
|
return normalize_dm_key_witness_payload(payload)
|
|
if event_type == "trust_vouch":
|
|
return normalize_trust_vouch_payload(payload)
|
|
if event_type == "key_rotate":
|
|
return normalize_key_rotate_payload(payload)
|
|
if event_type == "key_revoke":
|
|
return normalize_key_revoke_payload(payload)
|
|
if event_type == "abuse_report":
|
|
return normalize_abuse_report_payload(payload)
|
|
if event_type == "sar_anomaly":
|
|
return normalize_sar_anomaly_payload(payload)
|
|
return payload
|