"""Wormhole-owned DM contact and alias graph state.""" from __future__ import annotations import time from pathlib import Path from typing import Any from services.mesh.mesh_secure_storage import read_secure_json, write_secure_json DATA_DIR = Path(__file__).resolve().parents[2] / "data" CONTACTS_FILE = DATA_DIR / "wormhole_dm_contacts.json" TRUST_LEVELS = ( "unpinned", "tofu_pinned", "invite_pinned", "sas_verified", "mismatch", "continuity_broken", ) VERIFIED_FIRST_CONTACT_TRUST_LEVELS = ( "invite_pinned", "sas_verified", ) TRUST_RECOMMENDED_ACTIONS = { "unpinned": "import_invite", "tofu_pinned": "verify_sas", "invite_pinned": "show_sas", "sas_verified": "show_sas", "mismatch": "reverify", "continuity_broken": "reverify", } def _default_contact() -> dict[str, Any]: return { "alias": "", "blocked": False, "dhPubKey": "", "dhAlgo": "", "sharedAlias": "", "sharedAliasCounter": 0, "sharedAliasPublicKey": "", "sharedAliasPublicKeyAlgo": "Ed25519", "dmIdentityId": "", "previousSharedAliases": [], "pendingSharedAlias": "", "pendingSharedAliasCounter": 0, "pendingSharedAliasPublicKey": "", "pendingSharedAliasPublicKeyAlgo": "Ed25519", "pendingSharedAliasGraceMs": 0, "sharedAliasGraceUntil": 0, "sharedAliasRotatedAt": 0, "acceptedPreviousAlias": "", "acceptedPreviousAliasCounter": 0, "acceptedPreviousAliasPublicKey": "", "acceptedPreviousAliasPublicKeyAlgo": "Ed25519", "acceptedPreviousGraceUntil": 0, "acceptedPreviousHardGraceUntil": 0, "acceptedPreviousAwaitingReply": False, "aliasBindingSeq": 0, "aliasBindingPendingReason": "", "aliasBindingPreparedAt": 0, "aliasGateJoinAppliedSeq": 0, "trust_level": "unpinned", "verify_inband": False, "verify_registry": False, "verified": False, "verify_mismatch": False, "verified_at": 0, "invitePinnedTrustFingerprint": "", "invitePinnedNodeId": "", "invitePinnedPublicKey": "", "invitePinnedPublicKeyAlgo": "", "invitePinnedDhPubKey": "", "invitePinnedDhAlgo": "", "invitePinnedPrekeyLookupHandle": "", "invitePinnedRootFingerprint": "", "invitePinnedRootManifestFingerprint": "", "invitePinnedRootWitnessPolicyFingerprint": "", "invitePinnedRootWitnessThreshold": 0, "invitePinnedRootWitnessCount": 0, "invitePinnedRootWitnessDomainCount": 0, "invitePinnedRootManifestGeneration": 0, "invitePinnedRootRotationProven": False, "invitePinnedRootNodeId": "", "invitePinnedRootPublicKey": "", "invitePinnedRootPublicKeyAlgo": "", "invitePinnedIssuedAt": 0, "invitePinnedExpiresAt": 0, "invitePinnedAt": 0, "remotePrekeyFingerprint": "", "remotePrekeyObservedFingerprint": "", "remotePrekeyRootFingerprint": "", "remotePrekeyRootManifestFingerprint": "", "remotePrekeyRootWitnessPolicyFingerprint": "", "remotePrekeyRootWitnessThreshold": 0, "remotePrekeyRootWitnessCount": 0, "remotePrekeyRootWitnessDomainCount": 0, "remotePrekeyRootManifestGeneration": 0, "remotePrekeyRootRotationProven": False, "remotePrekeyObservedRootFingerprint": "", "remotePrekeyObservedRootManifestFingerprint": "", "remotePrekeyObservedRootWitnessPolicyFingerprint": "", "remotePrekeyObservedRootWitnessThreshold": 0, "remotePrekeyObservedRootWitnessCount": 0, "remotePrekeyObservedRootWitnessDomainCount": 0, "remotePrekeyObservedRootManifestGeneration": 0, "remotePrekeyObservedRootRotationProven": False, "remotePrekeyRootNodeId": "", "remotePrekeyRootPublicKey": "", "remotePrekeyRootPublicKeyAlgo": "", "remotePrekeyRootPinnedAt": 0, "remotePrekeyRootLastSeenAt": 0, "remotePrekeyRootMismatch": False, "remotePrekeyPinnedAt": 0, "remotePrekeyLastSeenAt": 0, "remotePrekeySequence": 0, "remotePrekeySignedAt": 0, "remotePrekeyMismatch": False, "remotePrekeyTransparencyHead": "", "remotePrekeyTransparencySize": 0, "remotePrekeyTransparencySeenAt": 0, "remotePrekeyTransparencyConflict": False, "remotePrekeyLookupMode": "", "witness_count": 0, "witness_checked_at": 0, "vouch_count": 0, "vouch_checked_at": 0, "updated_at": 0, } CLIENT_MUTABLE_CONTACT_FIELDS = frozenset( { "alias", "blocked", "dhPubKey", "dhAlgo", "sharedAlias", "sharedAliasCounter", "sharedAliasPublicKey", "sharedAliasPublicKeyAlgo", "previousSharedAliases", "pendingSharedAlias", "pendingSharedAliasCounter", "pendingSharedAliasPublicKey", "pendingSharedAliasPublicKeyAlgo", "pendingSharedAliasGraceMs", "sharedAliasGraceUntil", "sharedAliasRotatedAt", "acceptedPreviousAlias", "acceptedPreviousAliasCounter", "acceptedPreviousAliasPublicKey", "acceptedPreviousAliasPublicKeyAlgo", "acceptedPreviousGraceUntil", "acceptedPreviousHardGraceUntil", "acceptedPreviousAwaitingReply", "aliasBindingSeq", "aliasBindingPendingReason", "aliasBindingPreparedAt", "aliasGateJoinAppliedSeq", "verify_mismatch", "remotePrekeyTransparencyHead", "remotePrekeyTransparencySize", "remotePrekeyTransparencySeenAt", "remotePrekeyTransparencyConflict", "remotePrekeyLookupMode", "witness_count", "witness_checked_at", "vouch_count", "vouch_checked_at", } ) def _sanitize_client_contact_updates(updates: dict[str, Any] | None) -> dict[str, Any]: current = dict(updates or {}) sanitized: dict[str, Any] = {} for key in CLIENT_MUTABLE_CONTACT_FIELDS: if key in current: sanitized[key] = current[key] return sanitized def _contact_root_rotation_view(current: dict[str, Any]) -> tuple[int, bool]: root_mismatch = bool(current.get("remotePrekeyRootMismatch")) if root_mismatch: generation = int(current.get("remotePrekeyObservedRootManifestGeneration", 0) or 0) if generation <= 0: generation = int(current.get("remotePrekeyRootManifestGeneration", 0) or 0) if generation <= 0: return 0, False return generation, generation <= 1 or bool(current.get("remotePrekeyObservedRootRotationProven")) generation = int(current.get("remotePrekeyRootManifestGeneration", 0) or 0) proven = bool(current.get("remotePrekeyRootRotationProven")) if generation <= 0: generation = int(current.get("invitePinnedRootManifestGeneration", 0) or 0) proven = bool(current.get("invitePinnedRootRotationProven")) if generation <= 0: return 0, False return generation, generation <= 1 or proven def _contact_root_witness_view(current: dict[str, Any]) -> tuple[str, int, int, bool, int, bool]: root_mismatch = bool(current.get("remotePrekeyRootMismatch")) policy_fingerprint = "" witness_count = 0 witness_threshold = 0 witness_domain_count = 0 if root_mismatch: policy_fingerprint = str( current.get("remotePrekeyObservedRootWitnessPolicyFingerprint", "") or "" ).strip().lower() witness_count = int(current.get("remotePrekeyObservedRootWitnessCount", 0) or 0) witness_threshold = int(current.get("remotePrekeyObservedRootWitnessThreshold", 0) or 0) witness_domain_count = int(current.get("remotePrekeyObservedRootWitnessDomainCount", 0) or 0) else: policy_fingerprint = str(current.get("remotePrekeyRootWitnessPolicyFingerprint", "") or "").strip().lower() witness_count = int(current.get("remotePrekeyRootWitnessCount", 0) or 0) witness_threshold = int(current.get("remotePrekeyRootWitnessThreshold", 0) or 0) witness_domain_count = int(current.get("remotePrekeyRootWitnessDomainCount", 0) or 0) if witness_threshold <= 0: policy_fingerprint = policy_fingerprint or str( current.get("invitePinnedRootWitnessPolicyFingerprint", "") or "" ).strip().lower() witness_count = max(witness_count, int(current.get("invitePinnedRootWitnessCount", 0) or 0)) witness_threshold = max(witness_threshold, int(current.get("invitePinnedRootWitnessThreshold", 0) or 0)) witness_domain_count = max( witness_domain_count, int(current.get("invitePinnedRootWitnessDomainCount", 0) or 0), ) legacy_single_witness = witness_threshold <= 0 if legacy_single_witness: witness_threshold = 1 if witness_count <= 0: witness_count = 1 if witness_domain_count <= 0: witness_domain_count = 1 elif witness_count > 0 and witness_domain_count <= 0: witness_domain_count = 1 quorum_met = witness_threshold > 0 and witness_count >= witness_threshold independent_quorum_met = witness_threshold > 0 and witness_domain_count >= witness_threshold return ( policy_fingerprint, max(0, witness_count), max(0, witness_threshold), quorum_met, max(0, witness_domain_count), independent_quorum_met, ) def describe_contact_trust(contact: dict[str, Any] | None) -> dict[str, Any]: current = dict(contact or {}) from services.mesh.mesh_rollout_flags import wormhole_root_witness_finality_enforce from services.mesh.mesh_wormhole_root_manifest import root_witness_finality_met as root_witness_finality_met_view level = str(current.get("trust_level", "") or "").strip() if level not in TRUST_LEVELS: level = "unpinned" transparency_conflict = bool(current.get("remotePrekeyTransparencyConflict")) registry_mismatch = bool(current.get("verify_mismatch")) legacy_lookup = str(current.get("remotePrekeyLookupMode", "") or "").strip().lower() == "legacy_agent_id" root_attested = bool( str(current.get("invitePinnedRootFingerprint", "") or "").strip() or str(current.get("remotePrekeyRootFingerprint", "") or "").strip() ) root_witnessed = bool( str(current.get("invitePinnedRootManifestFingerprint", "") or "").strip() or str(current.get("remotePrekeyRootManifestFingerprint", "") or "").strip() or str(current.get("remotePrekeyObservedRootManifestFingerprint", "") or "").strip() ) root_mismatch = bool(current.get("remotePrekeyRootMismatch")) root_manifest_generation, root_rotation_proven = _contact_root_rotation_view(current) ( root_witness_policy_fingerprint, root_witness_count, root_witness_threshold, root_witness_quorum_met, root_witness_domain_count, root_witness_independent_quorum_met, ) = _contact_root_witness_view(current) if root_witnessed else ("", 0, 0, False, 0, False) root_rotation_unproven = bool(root_witnessed and root_manifest_generation > 1 and not root_rotation_proven) invite_attested = bool( str(current.get("invitePinnedTrustFingerprint", "") or "").strip() or int(current.get("invitePinnedAt", 0) or 0) > 0 ) if not root_attested: root_distribution_state = "none" elif not root_witnessed: root_distribution_state = "internal_only" elif not root_witness_quorum_met: root_distribution_state = "witness_policy_not_met" elif root_witness_threshold <= 1: root_distribution_state = "single_witness" else: root_distribution_state = "quorum_witnessed" if not root_attested: root_witness_provenance_state = "none" elif not root_witnessed: root_witness_provenance_state = "internal_only" elif not root_witness_quorum_met: root_witness_provenance_state = "witness_policy_not_met" elif root_witness_threshold <= 1: root_witness_provenance_state = "single_witness" elif root_witness_independent_quorum_met: root_witness_provenance_state = "independent_quorum" else: root_witness_provenance_state = "local_quorum" root_witness_finality_met = root_witness_finality_met_view( witness_threshold=root_witness_threshold, witness_quorum_met=root_witness_quorum_met, witness_independent_quorum_met=root_witness_independent_quorum_met, ) enforce_root_witness_finality = bool(wormhole_root_witness_finality_enforce()) root_distribution_upgrade_needed = bool( root_attested and root_distribution_state in ("internal_only", "single_witness", "witness_policy_not_met") ) root_finality_upgrade_needed = bool( root_attested and root_distribution_state == "quorum_witnessed" and root_witness_threshold > 1 and not root_witness_finality_met ) witnessed_root_label = ( "independently quorum-witnessed stable root identity" if root_witness_provenance_state == "independent_quorum" else ( "locally quorum-witnessed stable root identity" if root_witness_provenance_state == "local_quorum" else ( "single-witness stable root identity" if root_witness_provenance_state == "single_witness" else "witnessed stable root identity" ) ) ) label = "UNVERIFIED" severity = "warn" detail = "No trusted first-contact anchor. Import a signed invite before secure first contact." recommended_action = TRUST_RECOMMENDED_ACTIONS.get(level, "show_sas") if level == "tofu_pinned": label = "TOFU PINNED" detail = ( "First contact is pinned on first sight only. Verify SAS before sensitive use." if not root_attested else ( ( ( f"Current prekey is seen under one {witnessed_root_label}, but first contact is still TOFU-only. Verify SAS before sensitive use." if root_witness_provenance_state in ("independent_quorum", "local_quorum") else ( "Current prekey is seen under one single-witness stable root, but first contact is still TOFU-only. Re-import a current signed invite if you want stronger quorum witness provenance." if root_witness_provenance_state == "single_witness" else "Current prekey is seen under a witnessed stable root, but the current witness policy is not satisfied. Replace or re-import the signed invite before treating this root as strong first-contact provenance." ) ) if not root_rotation_unproven else "Current prekey is seen under one witnessed stable root, but that root rotation lacks previous-root proof. Replace the signed invite before treating this root as continuous." ) if root_witnessed else "Current prekey is seen under one stable root, but first contact is still TOFU-only. Verify SAS before sensitive use." ) ) elif level == "invite_pinned": label = "INVITE PINNED" detail = ( "First contact is anchored to an imported signed invite. SAS is optional but recommended for continuity." if not root_attested else ( ( ( f"First contact is anchored to an imported signed invite and an {witnessed_root_label}. SAS is optional but recommended for continuity." if root_witness_provenance_state in ("independent_quorum", "local_quorum") else ( "First contact is anchored to an imported signed invite and a single-witness stable root identity. Re-import a current signed invite if you want stronger quorum witness provenance." if root_witness_provenance_state == "single_witness" else "First contact is anchored to an imported signed invite and a witnessed stable root identity, but the current witness policy is not satisfied. Replace the signed invite before private use." ) ) if not root_rotation_unproven else "First contact is anchored to an imported signed invite and a witnessed stable root identity, but its current root rotation lacks previous-root proof. Replace the signed invite before private use." ) if root_witnessed else "First contact is anchored to an imported signed invite and a stable root identity. Re-import a current signed invite to refresh witnessed root distribution." ) ) if root_distribution_upgrade_needed or root_rotation_unproven: recommended_action = "import_invite" elif level == "sas_verified": label = "SAS VERIFIED" severity = "good" detail = ( "This contact was confirmed with a shared SAS phrase on the current pinned fingerprint." if not root_attested else ( ( ( f"This contact was SAS confirmed on the current pinned fingerprint and an {witnessed_root_label}." if root_witness_provenance_state in ("independent_quorum", "local_quorum") else ( "This contact was SAS confirmed on the current pinned fingerprint and single-witness stable root identity. Re-import a current signed invite if you want stronger quorum witness provenance." if root_witness_provenance_state == "single_witness" else "This contact was SAS confirmed on the current pinned fingerprint, but the current witnessed root does not satisfy its witness policy." ) ) if not root_rotation_unproven else "This contact was SAS confirmed on the current pinned fingerprint, but its current witnessed root rotation lacks previous-root proof." ) if root_witnessed else "This contact was SAS confirmed on the current pinned fingerprint and stable root identity, but its root distribution is still internal-only." ) ) if root_distribution_upgrade_needed or root_rotation_unproven: recommended_action = "import_invite" elif level == "mismatch": label = "REVERIFY" severity = "danger" detail = ( "Observed prekey identity changed. Compare SAS before trusting the new key." if not root_mismatch else ( ( f"Observed {witnessed_root_label} changed. Replace the invite or compare SAS before trusting the new key." if root_witness_provenance_state in ("independent_quorum", "local_quorum") else ( "Observed single-witness stable root identity changed. Replace the invite or compare SAS before trusting the new key." if root_witness_provenance_state == "single_witness" else "Observed stable root identity changed and its current witness policy is not satisfied. Replace the invite before trusting the new key." ) ) if not root_rotation_unproven else "Observed witnessed stable root rotation lacks previous-root proof. Replace the invite before trusting this root change." ) ) elif level == "continuity_broken": label = "CONTINUITY BROKEN" severity = "danger" detail = ( "Pinned trust anchor changed. Re-verify SAS or replace the invite before private use." if not root_mismatch else ( ( f"Pinned {witnessed_root_label} changed. Replace the signed invite or re-verify SAS before private use." if root_witness_provenance_state in ("independent_quorum", "local_quorum") else ( "Pinned single-witness stable root identity changed. Replace the signed invite or re-verify SAS before private use." if root_witness_provenance_state == "single_witness" else "Pinned stable root identity changed and its current witness policy is not satisfied. Replace the signed invite or re-verify SAS before private use." ) ) if not root_rotation_unproven else "Pinned witnessed stable root changed without previous-root proof. Replace the signed invite or re-verify SAS before private use." ) ) if transparency_conflict: detail = ( "Prekey transparency history conflicted. Trust stays degraded until you explicitly acknowledge the changed fingerprint." ) elif root_rotation_unproven and level not in ("mismatch", "continuity_broken"): recommended_action = "import_invite" elif root_distribution_state == "witness_policy_not_met" and level not in ("mismatch", "continuity_broken"): recommended_action = "import_invite" elif enforce_root_witness_finality and root_finality_upgrade_needed and level not in ("mismatch", "continuity_broken"): recommended_action = "import_invite" elif legacy_lookup and level not in ("mismatch", "continuity_broken"): detail = ( f"{detail} This contact still bootstraps through legacy direct agent ID lookup. " "Import or re-import a signed invite to avoid stable-ID lookup before removal." ) recommended_action = "import_invite" return { "state": level, "label": label, "severity": severity, "detail": detail, "verifiedFirstContact": ( level in VERIFIED_FIRST_CONTACT_TRUST_LEVELS and not root_rotation_unproven and root_distribution_state != "witness_policy_not_met" and not (enforce_root_witness_finality and root_finality_upgrade_needed) ), "recommendedAction": recommended_action, "legacyLookup": legacy_lookup, "inviteAttested": invite_attested, "rootAttested": root_attested, "rootWitnessed": root_witnessed, "rootDistributionState": root_distribution_state, "rootWitnessPolicyFingerprint": root_witness_policy_fingerprint, "rootWitnessCount": root_witness_count, "rootWitnessThreshold": root_witness_threshold, "rootWitnessQuorumMet": root_witness_quorum_met, "rootWitnessProvenanceState": root_witness_provenance_state, "rootWitnessDomainCount": root_witness_domain_count, "rootWitnessIndependentQuorumMet": root_witness_independent_quorum_met, "rootWitnessFinalityMet": root_witness_finality_met, "rootManifestGeneration": root_manifest_generation, "rootRotationProven": root_rotation_proven, "rootMismatch": root_mismatch, "registryMismatch": registry_mismatch, "transparencyConflict": transparency_conflict, } def describe_contact_alias_state(contact: dict[str, Any] | None) -> dict[str, Any]: current = dict(contact or {}) trust_summary = dict(current.get("trustSummary") or {}) now_ms = int(time.time() * 1000) active_alias = str(current.get("sharedAlias", "") or "").strip() pending_alias = str(current.get("pendingSharedAlias", "") or "").strip() grace_until = int(current.get("sharedAliasGraceUntil", 0) or 0) rotated_at = int(current.get("sharedAliasRotatedAt", 0) or 0) has_peer_dh = bool( str( current.get("dhPubKey") or current.get("invitePinnedDhPubKey") or "" ).strip() ) verified_first_contact = bool(trust_summary.get("verifiedFirstContact")) pending_active = bool(pending_alias) grace_remaining_ms = max(0, grace_until - now_ms) if pending_alias and grace_until > 0 else 0 can_prepare_issue = bool(not active_alias and has_peer_dh and not pending_active) can_prepare_rotation = bool(active_alias and has_peer_dh and not pending_active) background_prepare_allowed = bool( verified_first_contact and (can_prepare_issue or can_prepare_rotation) ) if pending_active: state = "pending_promotion" recommended_action = "wait_for_promotion" elif not has_peer_dh: state = "needs_peer_dh" recommended_action = "refresh_contact" elif not active_alias: state = "ready_to_issue" recommended_action = ( "issue_alias" if verified_first_contact else str(trust_summary.get("recommendedAction", "") or "verify_contact") ) else: state = "active" recommended_action = ( "rotate_when_needed" if verified_first_contact else str(trust_summary.get("recommendedAction", "") or "verify_contact") ) return { "state": state, "hasActiveAlias": bool(active_alias), "hasPendingAlias": bool(pending_alias), "graceUntil": grace_until, "graceRemainingMs": grace_remaining_ms, "lastRotatedAt": rotated_at, "hasPeerDh": has_peer_dh, "verifiedFirstContact": verified_first_contact, "canPrepareIssue": can_prepare_issue, "canPrepareRotation": can_prepare_rotation, "backgroundPrepareAllowed": background_prepare_allowed, "recommendedAction": recommended_action, } def accepted_contact_shared_aliases( contact: dict[str, Any] | None, *, now_ms: int | None = None, ) -> list[str]: current = _normalize_contact(contact) accepted: list[str] = [] active_alias = str(current.get("sharedAlias", "") or "").strip() pending_alias = str(current.get("pendingSharedAlias", "") or "").strip() grace_until = int(current.get("sharedAliasGraceUntil", 0) or 0) previous_alias = str(current.get("acceptedPreviousAlias", "") or "").strip() previous_grace_until = int(current.get("acceptedPreviousGraceUntil", 0) or 0) previous_hard_grace_until = int(current.get("acceptedPreviousHardGraceUntil", 0) or 0) previous_awaiting_reply = bool(current.get("acceptedPreviousAwaitingReply")) if active_alias: accepted.append(active_alias) current_ms = int(now_ms) if now_ms is not None else int(time.time() * 1000) if pending_alias and grace_until > current_ms and pending_alias not in accepted: accepted.append(pending_alias) if previous_alias and previous_alias not in accepted: within_default_grace = previous_grace_until > current_ms within_hard_cap = previous_awaiting_reply and previous_hard_grace_until > current_ms if within_default_grace or within_hard_cap: accepted.append(previous_alias) return accepted def contact_shared_alias_accepted( contact: dict[str, Any] | None, alias: str, *, now_ms: int | None = None, ) -> bool: alias_key = str(alias or "").strip() if not alias_key: return False return alias_key in accepted_contact_shared_aliases(contact, now_ms=now_ms) def _normalize_contact(value: dict[str, Any] | None) -> dict[str, Any]: defaults = _default_contact() merged = dict(defaults) if isinstance(value, dict): merged.update(value) current = {key: merged.get(key, defaults[key]) for key in defaults.keys()} current["alias"] = str(current.get("alias", "") or "") current["blocked"] = bool(current.get("blocked")) current["dhPubKey"] = str(current.get("dhPubKey", "") or "") current["dhAlgo"] = str(current.get("dhAlgo", "") or "") current["sharedAlias"] = str(current.get("sharedAlias", "") or "") current["sharedAliasCounter"] = int(current.get("sharedAliasCounter", 0) or 0) current["sharedAliasPublicKey"] = str(current.get("sharedAliasPublicKey", "") or "") current["sharedAliasPublicKeyAlgo"] = str(current.get("sharedAliasPublicKeyAlgo", "Ed25519") or "Ed25519") raw_dm_identity_id = str( current.get("dmIdentityId", "") or merged.get("dm_identity_id", "") or "" ).strip() if raw_dm_identity_id: try: from services.mesh.mesh_wormhole_dead_drop import dead_drop_redact_label current["dmIdentityId"] = dead_drop_redact_label(raw_dm_identity_id) except Exception: current["dmIdentityId"] = raw_dm_identity_id else: current["dmIdentityId"] = "" current["previousSharedAliases"] = [ str(item or "") for item in list(current.get("previousSharedAliases") or []) if str(item or "").strip() ][-2:] current["pendingSharedAlias"] = str(current.get("pendingSharedAlias", "") or "") current["pendingSharedAliasCounter"] = int(current.get("pendingSharedAliasCounter", 0) or 0) current["pendingSharedAliasPublicKey"] = str(current.get("pendingSharedAliasPublicKey", "") or "") current["pendingSharedAliasPublicKeyAlgo"] = str( current.get("pendingSharedAliasPublicKeyAlgo", "Ed25519") or "Ed25519" ) current["pendingSharedAliasGraceMs"] = int(current.get("pendingSharedAliasGraceMs", 0) or 0) current["acceptedPreviousAlias"] = str(current.get("acceptedPreviousAlias", "") or "") current["acceptedPreviousAliasCounter"] = int(current.get("acceptedPreviousAliasCounter", 0) or 0) current["acceptedPreviousAliasPublicKey"] = str(current.get("acceptedPreviousAliasPublicKey", "") or "") current["acceptedPreviousAliasPublicKeyAlgo"] = str( current.get("acceptedPreviousAliasPublicKeyAlgo", "Ed25519") or "Ed25519" ) current["aliasBindingSeq"] = int(current.get("aliasBindingSeq", 0) or 0) current["aliasBindingPendingReason"] = str(current.get("aliasBindingPendingReason", "") or "") current["invitePinnedTrustFingerprint"] = str(current.get("invitePinnedTrustFingerprint", "") or "").strip().lower() current["invitePinnedNodeId"] = str(current.get("invitePinnedNodeId", "") or "") current["invitePinnedPublicKey"] = str(current.get("invitePinnedPublicKey", "") or "") current["invitePinnedPublicKeyAlgo"] = str(current.get("invitePinnedPublicKeyAlgo", "") or "") current["invitePinnedDhPubKey"] = str(current.get("invitePinnedDhPubKey", "") or "") current["invitePinnedDhAlgo"] = str(current.get("invitePinnedDhAlgo", "") or "") current["invitePinnedPrekeyLookupHandle"] = str(current.get("invitePinnedPrekeyLookupHandle", "") or "") current["invitePinnedRootFingerprint"] = str(current.get("invitePinnedRootFingerprint", "") or "").strip().lower() current["invitePinnedRootManifestFingerprint"] = str( current.get("invitePinnedRootManifestFingerprint", "") or "" ).strip().lower() current["invitePinnedRootWitnessPolicyFingerprint"] = str( current.get("invitePinnedRootWitnessPolicyFingerprint", "") or "" ).strip().lower() current["invitePinnedRootNodeId"] = str(current.get("invitePinnedRootNodeId", "") or "") current["invitePinnedRootPublicKey"] = str(current.get("invitePinnedRootPublicKey", "") or "") current["invitePinnedRootPublicKeyAlgo"] = str(current.get("invitePinnedRootPublicKeyAlgo", "") or "") current["remotePrekeyFingerprint"] = str(current.get("remotePrekeyFingerprint", "") or "") current["remotePrekeyObservedFingerprint"] = str(current.get("remotePrekeyObservedFingerprint", "") or "") current["remotePrekeyRootFingerprint"] = str(current.get("remotePrekeyRootFingerprint", "") or "").strip().lower() current["remotePrekeyRootManifestFingerprint"] = str( current.get("remotePrekeyRootManifestFingerprint", "") or "" ).strip().lower() current["remotePrekeyRootWitnessPolicyFingerprint"] = str( current.get("remotePrekeyRootWitnessPolicyFingerprint", "") or "" ).strip().lower() current["remotePrekeyObservedRootFingerprint"] = str( current.get("remotePrekeyObservedRootFingerprint", "") or "" ).strip().lower() current["remotePrekeyObservedRootManifestFingerprint"] = str( current.get("remotePrekeyObservedRootManifestFingerprint", "") or "" ).strip().lower() current["remotePrekeyObservedRootWitnessPolicyFingerprint"] = str( current.get("remotePrekeyObservedRootWitnessPolicyFingerprint", "") or "" ).strip().lower() current["remotePrekeyRootNodeId"] = str(current.get("remotePrekeyRootNodeId", "") or "") current["remotePrekeyRootPublicKey"] = str(current.get("remotePrekeyRootPublicKey", "") or "") current["remotePrekeyRootPublicKeyAlgo"] = str(current.get("remotePrekeyRootPublicKeyAlgo", "") or "") current["remotePrekeyTransparencyHead"] = str( current.get("remotePrekeyTransparencyHead", "") or "" ).strip().lower() current["remotePrekeyLookupMode"] = str(current.get("remotePrekeyLookupMode", "") or "").strip().lower() tl = str(current.get("trust_level", "") or "").strip() current["trust_level"] = tl if tl in TRUST_LEVELS else "unpinned" for key in ( "sharedAliasGraceUntil", "sharedAliasRotatedAt", "acceptedPreviousGraceUntil", "acceptedPreviousHardGraceUntil", "aliasBindingPreparedAt", "aliasGateJoinAppliedSeq", "verified_at", "invitePinnedIssuedAt", "invitePinnedExpiresAt", "invitePinnedAt", "invitePinnedRootManifestGeneration", "invitePinnedRootWitnessThreshold", "invitePinnedRootWitnessCount", "invitePinnedRootWitnessDomainCount", "remotePrekeyRootPinnedAt", "remotePrekeyRootLastSeenAt", "remotePrekeyRootWitnessThreshold", "remotePrekeyRootWitnessCount", "remotePrekeyRootWitnessDomainCount", "remotePrekeyRootManifestGeneration", "remotePrekeyObservedRootWitnessThreshold", "remotePrekeyObservedRootWitnessCount", "remotePrekeyObservedRootWitnessDomainCount", "remotePrekeyObservedRootManifestGeneration", "remotePrekeyPinnedAt", "remotePrekeyLastSeenAt", "remotePrekeySequence", "remotePrekeySignedAt", "remotePrekeyTransparencySize", "remotePrekeyTransparencySeenAt", "witness_count", "witness_checked_at", "vouch_count", "vouch_checked_at", "updated_at", ): current[key] = int(current.get(key, 0) or 0) for key in ( "verify_inband", "verify_registry", "verified", "verify_mismatch", "acceptedPreviousAwaitingReply", "invitePinnedRootRotationProven", "remotePrekeyRootMismatch", "remotePrekeyRootRotationProven", "remotePrekeyObservedRootRotationProven", "remotePrekeyMismatch", "remotePrekeyTransparencyConflict", ): current[key] = bool(current.get(key)) current["trustSummary"] = describe_contact_trust(current) current["aliasSummary"] = describe_contact_alias_state(current) return current def get_contact_trust_level(peer_id: str) -> str: peer_key = str(peer_id or "").strip() if not peer_key: return "unpinned" contacts = _read_contacts() current = _normalize_contact(contacts.get(peer_key)) return str(current.get("trust_level", "") or "").strip() or "unpinned" def verified_first_contact_requirement(peer_id: str = "", trust_level: str | None = None) -> dict[str, Any]: peer_key = str(peer_id or "").strip() if peer_key: contacts = _read_contacts() current = _normalize_contact(contacts.get(peer_key)) trust_summary = dict(current.get("trustSummary") or {}) state = str(trust_summary.get("state", current.get("trust_level", "")) or "").strip() or "unpinned" if bool(trust_summary.get("verifiedFirstContact")): return { "ok": True, "trust_level": state, } if state in ("mismatch", "continuity_broken"): return { "ok": False, "trust_level": state, "detail": "remote prekey identity changed; verification required", } if bool(trust_summary.get("rootWitnessed")) and int(trust_summary.get("rootManifestGeneration", 0) or 0) > 1 and not bool( trust_summary.get("rootRotationProven") ): return { "ok": False, "trust_level": state, "detail": str( trust_summary.get("detail", "") or "current witnessed root rotation lacks previous-root proof", ), } if ( state in VERIFIED_FIRST_CONTACT_TRUST_LEVELS and str(trust_summary.get("rootDistributionState", "") or "") == "quorum_witnessed" and int(trust_summary.get("rootWitnessThreshold", 0) or 0) > 1 and not bool(trust_summary.get("rootWitnessFinalityMet")) ): return { "ok": False, "trust_level": state, "detail": "independent quorum root witness finality required before secure first contact", } return { "ok": False, "trust_level": state, "detail": "signed invite or SAS verification required before secure first contact", } level = str(trust_level or "").strip() or get_contact_trust_level(peer_id) if level in VERIFIED_FIRST_CONTACT_TRUST_LEVELS: return { "ok": True, "trust_level": level, } if level in ("mismatch", "continuity_broken"): return { "ok": False, "trust_level": level, "detail": "remote prekey identity changed; verification required", } return { "ok": False, "trust_level": level or "unpinned", "detail": "signed invite or SAS verification required before secure first contact", } def _merge_alias_history(*aliases: str, limit: int = 2) -> list[str]: unique: set[str] = set() ordered: list[str] = [] for alias in aliases: value = str(alias or "").strip() if not value or value in unique: continue unique.add(value) ordered.append(value) if len(ordered) >= limit: break return ordered def _promote_pending_alias_if_due(contact: dict[str, Any]) -> tuple[dict[str, Any], bool]: return _normalize_contact(contact), False def _read_contacts() -> dict[str, dict[str, Any]]: try: raw = read_secure_json(CONTACTS_FILE, lambda: {}) except Exception: import logging logging.getLogger(__name__).warning( "Contacts file could not be decrypted — starting with empty contacts" ) CONTACTS_FILE.unlink(missing_ok=True) return {} if not isinstance(raw, dict): return {} contacts: dict[str, dict[str, Any]] = {} changed = False for peer_id, value in raw.items(): key = str(peer_id or "").strip() if not key: continue normalized, promoted = _promote_pending_alias_if_due(value if isinstance(value, dict) else {}) invite_lookup_upgraded = _promote_invite_lookup_mode(normalized) contacts[key] = normalized changed = changed or promoted or invite_lookup_upgraded if changed: _write_contacts(contacts) return contacts def _write_contacts(contacts: dict[str, dict[str, Any]]) -> None: DATA_DIR.mkdir(parents=True, exist_ok=True) payload: dict[str, dict[str, Any]] = {} for peer_id, contact in contacts.items(): key = str(peer_id or "").strip() if not key: continue normalized = _normalize_contact(contact) normalized.pop("trustSummary", None) normalized.pop("aliasSummary", None) payload[key] = normalized write_secure_json(CONTACTS_FILE, payload) def _normalize_sas_phrase(value: str) -> str: return " ".join(str(value or "").strip().lower().split()) def _derive_expected_contact_sas_phrase( peer_id: str, *, peer_ref: str = "", words: int = 8, peer_dh_pub_override: str = "", ) -> dict[str, Any]: peer_key = str(peer_id or "").strip() if not peer_key: raise ValueError("peer_id required") contacts = _read_contacts() current = _normalize_contact(contacts.get(peer_key)) peer_dh_pub = str( peer_dh_pub_override or current.get("dhPubKey") or current.get("invitePinnedDhPubKey") or "" ).strip() if not peer_dh_pub: return { "ok": False, "detail": "peer dh identity unavailable for sas verification", } from services.mesh.mesh_wormhole_dead_drop import derive_sas_phrase return derive_sas_phrase( peer_id=peer_key, peer_dh_pub=peer_dh_pub, words=words, peer_ref=str(peer_ref or ""), ) def list_wormhole_dm_contacts() -> dict[str, dict[str, Any]]: return _read_contacts() def _promote_invite_lookup_mode(contact: dict[str, Any], *, now: int | None = None) -> bool: current = dict(contact or {}) lookup_handle = str(current.get("invitePinnedPrekeyLookupHandle", "") or "").strip() if not lookup_handle: return False if str(current.get("remotePrekeyLookupMode", "") or "").strip().lower() == "invite_lookup_handle": return False current["remotePrekeyLookupMode"] = "invite_lookup_handle" current["updated_at"] = int(now if now is not None else time.time()) contact.clear() contact.update(_normalize_contact(current)) return True def upgrade_invite_scoped_contact_preferences() -> int: contacts = _read_contacts() now = int(time.time()) changed = 0 for peer_id, raw_contact in list(contacts.items()): current = _normalize_contact(raw_contact) if _promote_invite_lookup_mode(current, now=now): contacts[peer_id] = current changed += 1 if changed: _write_contacts(contacts) return changed def preferred_prekey_lookup_handle(peer_id: str) -> str: peer_key = str(peer_id or "").strip() if not peer_key: return "" contacts = _read_contacts() current = _normalize_contact(contacts.get(peer_key)) if _promote_invite_lookup_mode(current): contacts[peer_key] = current _write_contacts(contacts) return str(current.get("invitePinnedPrekeyLookupHandle", "") or "").strip() def compatibility_lookup_readiness_snapshot() -> dict[str, Any]: contacts = _read_contacts() stored_legacy_lookup_contacts = 0 stored_invite_lookup_contacts = 0 for raw_contact in list(contacts.values()): current = _normalize_contact(raw_contact) lookup_mode = str(current.get("remotePrekeyLookupMode", "") or "").strip().lower() lookup_handle = str(current.get("invitePinnedPrekeyLookupHandle", "") or "").strip() if lookup_handle: stored_invite_lookup_contacts += 1 if lookup_mode == "legacy_agent_id": stored_legacy_lookup_contacts += 1 return { "stored_legacy_lookup_contacts_present": stored_legacy_lookup_contacts > 0, "stored_legacy_lookup_contacts": stored_legacy_lookup_contacts, "stored_invite_lookup_contacts": stored_invite_lookup_contacts, } def upsert_wormhole_dm_contact(peer_id: str, updates: dict[str, Any]) -> dict[str, Any]: return _upsert_wormhole_dm_contact(peer_id, updates, sanitize_updates=True) def upsert_wormhole_dm_contact_internal(peer_id: str, updates: dict[str, Any]) -> dict[str, Any]: return _upsert_wormhole_dm_contact(peer_id, updates, sanitize_updates=False) def _upsert_wormhole_dm_contact( peer_id: str, updates: dict[str, Any], *, sanitize_updates: bool, ) -> dict[str, Any]: peer_id = str(peer_id or "").strip() if not peer_id: raise ValueError("peer_id required") contacts = _read_contacts() current = _normalize_contact(contacts.get(peer_id)) safe_updates = _sanitize_client_contact_updates(updates) if sanitize_updates else dict(updates or {}) merged = _normalize_contact({**current, **safe_updates}) merged["updated_at"] = int(time.time()) contacts[peer_id] = merged _write_contacts(contacts) return merged def roll_forward_invite_lookup_handles( mapping: dict[str, str] | None, *, invite_node_id: str = "", ) -> int: current_mapping = { str(old or "").strip(): str(new or "").strip() for old, new in dict(mapping or {}).items() if str(old or "").strip() and str(new or "").strip() and str(old or "").strip() != str(new or "").strip() } if not current_mapping: return 0 expected_node_id = str(invite_node_id or "").strip() contacts = _read_contacts() now = int(time.time()) changed = 0 for peer_id, raw_contact in list(contacts.items()): current = _normalize_contact(raw_contact) if expected_node_id and str(current.get("invitePinnedNodeId", "") or "").strip() != expected_node_id: continue old_handle = str(current.get("invitePinnedPrekeyLookupHandle", "") or "").strip() new_handle = current_mapping.get(old_handle, "") if not new_handle: continue current["invitePinnedPrekeyLookupHandle"] = new_handle current["updated_at"] = now contacts[peer_id] = _normalize_contact(current) changed += 1 if changed: _write_contacts(contacts) return changed def pin_wormhole_dm_invite( peer_id: str, *, invite_payload: dict[str, Any], alias: str = "", attested: bool = True, ) -> dict[str, Any]: peer_key = str(peer_id or "").strip() if not peer_key: raise ValueError("peer_id required") payload = dict(invite_payload or {}) trust_fingerprint = str(payload.get("trust_fingerprint", "") or "").strip().lower() if not trust_fingerprint: raise ValueError("invite trust_fingerprint required") contacts = _read_contacts() current = _normalize_contact(contacts.get(peer_key)) now = int(time.time()) trust_level = "invite_pinned" if bool(attested) else "tofu_pinned" identity_dh_pub_key = str(payload.get("identity_dh_pub_key", "") or "") dh_algo = str(payload.get("dh_algo", "X25519") or "X25519") prekey_lookup_handle = str(payload.get("prekey_lookup_handle", "") or "") if str(alias or "").strip(): current["alias"] = str(alias or "").strip() current["dhPubKey"] = identity_dh_pub_key current["dhAlgo"] = dh_algo current["invitePinnedPrekeyLookupHandle"] = prekey_lookup_handle current["invitePinnedRootFingerprint"] = str(payload.get("root_fingerprint", "") or "").strip().lower() current["invitePinnedRootManifestFingerprint"] = str( payload.get("root_manifest_fingerprint", "") or "" ).strip().lower() current["invitePinnedRootWitnessPolicyFingerprint"] = str( payload.get("root_witness_policy_fingerprint", "") or "" ).strip().lower() current["invitePinnedRootWitnessThreshold"] = int(payload.get("root_witness_threshold", 0) or 0) current["invitePinnedRootWitnessCount"] = int(payload.get("root_witness_count", 0) or 0) current["invitePinnedRootWitnessDomainCount"] = int(payload.get("root_witness_domain_count", 0) or 0) current["invitePinnedRootManifestGeneration"] = int(payload.get("root_manifest_generation", 0) or 0) current["invitePinnedRootRotationProven"] = bool( int(payload.get("root_manifest_generation", 0) or 0) <= 1 or payload.get("root_rotation_proven") ) current["invitePinnedRootNodeId"] = str(payload.get("root_node_id", "") or "") current["invitePinnedRootPublicKey"] = str(payload.get("root_public_key", "") or "") current["invitePinnedRootPublicKeyAlgo"] = str(payload.get("root_public_key_algo", "Ed25519") or "Ed25519") current["invitePinnedIssuedAt"] = int(payload.get("issued_at", 0) or 0) current["invitePinnedExpiresAt"] = int(payload.get("expires_at", 0) or 0) current["remotePrekeyLookupMode"] = "invite_lookup_handle" if prekey_lookup_handle else "" current["remotePrekeyFingerprint"] = trust_fingerprint current["remotePrekeyObservedFingerprint"] = trust_fingerprint current["remotePrekeyRootFingerprint"] = str(payload.get("root_fingerprint", "") or "").strip().lower() current["remotePrekeyObservedRootFingerprint"] = str(payload.get("root_fingerprint", "") or "").strip().lower() current["remotePrekeyRootManifestFingerprint"] = str( payload.get("root_manifest_fingerprint", "") or "" ).strip().lower() current["remotePrekeyRootWitnessPolicyFingerprint"] = str( payload.get("root_witness_policy_fingerprint", "") or "" ).strip().lower() current["remotePrekeyRootWitnessThreshold"] = int(payload.get("root_witness_threshold", 0) or 0) current["remotePrekeyRootWitnessCount"] = int(payload.get("root_witness_count", 0) or 0) current["remotePrekeyRootWitnessDomainCount"] = int(payload.get("root_witness_domain_count", 0) or 0) current["remotePrekeyObservedRootManifestFingerprint"] = str( payload.get("root_manifest_fingerprint", "") or "" ).strip().lower() current["remotePrekeyObservedRootWitnessPolicyFingerprint"] = str( payload.get("root_witness_policy_fingerprint", "") or "" ).strip().lower() current["remotePrekeyObservedRootWitnessThreshold"] = int(payload.get("root_witness_threshold", 0) or 0) current["remotePrekeyObservedRootWitnessCount"] = int(payload.get("root_witness_count", 0) or 0) current["remotePrekeyObservedRootWitnessDomainCount"] = int(payload.get("root_witness_domain_count", 0) or 0) current["remotePrekeyRootManifestGeneration"] = int(payload.get("root_manifest_generation", 0) or 0) current["remotePrekeyObservedRootManifestGeneration"] = int(payload.get("root_manifest_generation", 0) or 0) current["remotePrekeyRootRotationProven"] = bool( int(payload.get("root_manifest_generation", 0) or 0) <= 1 or payload.get("root_rotation_proven") ) current["remotePrekeyObservedRootRotationProven"] = bool( int(payload.get("root_manifest_generation", 0) or 0) <= 1 or payload.get("root_rotation_proven") ) current["remotePrekeyRootNodeId"] = str(payload.get("root_node_id", "") or "") current["remotePrekeyRootPublicKey"] = str(payload.get("root_public_key", "") or "") current["remotePrekeyRootPublicKeyAlgo"] = str(payload.get("root_public_key_algo", "Ed25519") or "Ed25519") current["remotePrekeyRootPinnedAt"] = now current["remotePrekeyRootLastSeenAt"] = now current["remotePrekeyRootMismatch"] = False current["remotePrekeyPinnedAt"] = now current["remotePrekeyLastSeenAt"] = now current["remotePrekeyMismatch"] = False current["trust_level"] = trust_level if attested: current["invitePinnedTrustFingerprint"] = trust_fingerprint current["invitePinnedNodeId"] = peer_key current["invitePinnedPublicKey"] = str(payload.get("public_key", "") or "") current["invitePinnedPublicKeyAlgo"] = str(payload.get("public_key_algo", "Ed25519") or "Ed25519") current["invitePinnedDhPubKey"] = identity_dh_pub_key current["invitePinnedDhAlgo"] = dh_algo current["invitePinnedAt"] = now else: current["invitePinnedTrustFingerprint"] = "" current["invitePinnedNodeId"] = "" current["invitePinnedPublicKey"] = "" current["invitePinnedPublicKeyAlgo"] = "" current["invitePinnedDhPubKey"] = "" current["invitePinnedDhAlgo"] = "" current["invitePinnedRootFingerprint"] = "" current["invitePinnedRootManifestFingerprint"] = "" current["invitePinnedRootWitnessPolicyFingerprint"] = "" current["invitePinnedRootWitnessThreshold"] = 0 current["invitePinnedRootWitnessCount"] = 0 current["invitePinnedRootWitnessDomainCount"] = 0 current["invitePinnedRootManifestGeneration"] = 0 current["invitePinnedRootRotationProven"] = False current["invitePinnedRootNodeId"] = "" current["invitePinnedRootPublicKey"] = "" current["invitePinnedRootPublicKeyAlgo"] = "" current["invitePinnedAt"] = 0 current["verified"] = False current["verify_inband"] = False current["verify_registry"] = False current["verify_mismatch"] = False current["verified_at"] = 0 current["updated_at"] = now contacts[peer_key] = _normalize_contact(current) _write_contacts(contacts) return contacts[peer_key] def delete_wormhole_dm_contact(peer_id: str) -> bool: peer_id = str(peer_id or "").strip() if not peer_id: return False contacts = _read_contacts() if peer_id not in contacts: return False del contacts[peer_id] _write_contacts(contacts) return True def observe_remote_prekey_identity( peer_id: str, *, fingerprint: str, sequence: int = 0, signed_at: int = 0, transparency_head: str = "", transparency_size: int = 0, witness_count: int | None = None, witness_latest_at: int = 0, root_fingerprint: str = "", root_manifest_fingerprint: str = "", root_witness_policy_fingerprint: str = "", root_witness_threshold: int = 0, root_witness_count: int = 0, root_witness_domain_count: int = 0, root_manifest_generation: int = 0, root_rotation_proven: bool = False, root_node_id: str = "", root_public_key: str = "", root_public_key_algo: str = "Ed25519", ) -> dict[str, Any]: peer_key = str(peer_id or "").strip() candidate = str(fingerprint or "").strip().lower() if not peer_key: raise ValueError("peer_id required") if not candidate: raise ValueError("fingerprint required") contacts = _read_contacts() current = _normalize_contact(contacts.get(peer_key)) now = int(time.time()) pinned = str(current.get("remotePrekeyFingerprint", "") or "").strip().lower() invite_pinned = str(current.get("invitePinnedTrustFingerprint", "") or "").strip().lower() pinned_root = str(current.get("remotePrekeyRootFingerprint", "") or "").strip().lower() pinned_root_manifest = str(current.get("remotePrekeyRootManifestFingerprint", "") or "").strip().lower() invite_pinned_root = str(current.get("invitePinnedRootFingerprint", "") or "").strip().lower() invite_pinned_root_manifest = str(current.get("invitePinnedRootManifestFingerprint", "") or "").strip().lower() observed_root = str(root_fingerprint or "").strip().lower() observed_root_manifest = str(root_manifest_fingerprint or "").strip().lower() observed_root_witness_policy = str(root_witness_policy_fingerprint or "").strip().lower() observed_root_witness_threshold = int(root_witness_threshold or 0) observed_root_witness_count = int(root_witness_count or 0) observed_root_witness_domain_count = int(root_witness_domain_count or 0) observed_root_manifest_generation = int(root_manifest_generation or 0) observed_root_rotation_proven = bool(observed_root_manifest_generation <= 1 or root_rotation_proven) prior_root_mismatch = bool(current.get("remotePrekeyRootMismatch")) prior_sequence = int(current.get("remotePrekeySequence", 0) or 0) prior_transparency_head = str(current.get("remotePrekeyTransparencyHead", "") or "").strip().lower() prior_transparency_size = int(current.get("remotePrekeyTransparencySize", 0) or 0) prior_transparency_conflict = bool(current.get("remotePrekeyTransparencyConflict")) observed_transparency_head = str(transparency_head or "").strip().lower() observed_transparency_size = int(transparency_size or 0) observed_sequence = int(sequence or 0) observed_signed_at = int(signed_at or 0) transparency_conflict = False if observed_transparency_head: if prior_sequence > 0 and int(sequence or 0) > 0 and int(sequence or 0) < prior_sequence: transparency_conflict = True elif ( prior_sequence > 0 and int(sequence or 0) > 0 and int(sequence or 0) == prior_sequence and prior_transparency_head and observed_transparency_head != prior_transparency_head ): transparency_conflict = True elif prior_transparency_size > 0 and observed_transparency_size > 0 and observed_transparency_size < prior_transparency_size: transparency_conflict = True current["remotePrekeyObservedFingerprint"] = candidate current["remotePrekeyLastSeenAt"] = now if observed_root: current["remotePrekeyObservedRootFingerprint"] = observed_root current["remotePrekeyObservedRootManifestFingerprint"] = observed_root_manifest current["remotePrekeyObservedRootWitnessPolicyFingerprint"] = observed_root_witness_policy current["remotePrekeyObservedRootWitnessThreshold"] = observed_root_witness_threshold current["remotePrekeyObservedRootWitnessCount"] = observed_root_witness_count current["remotePrekeyObservedRootWitnessDomainCount"] = observed_root_witness_domain_count current["remotePrekeyObservedRootManifestGeneration"] = observed_root_manifest_generation current["remotePrekeyObservedRootRotationProven"] = observed_root_rotation_proven current["remotePrekeyRootLastSeenAt"] = now current["remotePrekeyRootNodeId"] = str(root_node_id or "") current["remotePrekeyRootPublicKey"] = str(root_public_key or "") current["remotePrekeyRootPublicKeyAlgo"] = str(root_public_key_algo or "Ed25519") if not transparency_conflict: current["remotePrekeySequence"] = observed_sequence current["remotePrekeySignedAt"] = observed_signed_at if observed_transparency_head and not transparency_conflict: current["remotePrekeyTransparencyHead"] = observed_transparency_head current["remotePrekeyTransparencySize"] = observed_transparency_size current["remotePrekeyTransparencySeenAt"] = now current["remotePrekeyTransparencyConflict"] = transparency_conflict if witness_count is not None: current["witness_count"] = max(0, int(witness_count or 0)) current["witness_checked_at"] = int(witness_latest_at or now) prior_trust = str(current.get("trust_level", "") or "").strip() trust_changed = False if not pinned and invite_pinned: current["remotePrekeyFingerprint"] = invite_pinned current["remotePrekeyPinnedAt"] = int(current.get("invitePinnedAt", 0) or now) pinned = invite_pinned if not pinned_root and invite_pinned_root: current["remotePrekeyRootFingerprint"] = invite_pinned_root current["remotePrekeyRootManifestFingerprint"] = invite_pinned_root_manifest current["remotePrekeyRootWitnessPolicyFingerprint"] = str( current.get("invitePinnedRootWitnessPolicyFingerprint", "") or "" ).strip().lower() current["remotePrekeyRootWitnessThreshold"] = int(current.get("invitePinnedRootWitnessThreshold", 0) or 0) current["remotePrekeyRootWitnessCount"] = int(current.get("invitePinnedRootWitnessCount", 0) or 0) current["remotePrekeyRootWitnessDomainCount"] = int( current.get("invitePinnedRootWitnessDomainCount", 0) or 0 ) current["remotePrekeyRootManifestGeneration"] = int(current.get("invitePinnedRootManifestGeneration", 0) or 0) current["remotePrekeyRootRotationProven"] = bool( int(current.get("invitePinnedRootManifestGeneration", 0) or 0) <= 1 or current.get("invitePinnedRootRotationProven") ) current["remotePrekeyRootPinnedAt"] = int(current.get("invitePinnedAt", 0) or now) current["remotePrekeyRootNodeId"] = str(current.get("invitePinnedRootNodeId", "") or "") current["remotePrekeyRootPublicKey"] = str(current.get("invitePinnedRootPublicKey", "") or "") current["remotePrekeyRootPublicKeyAlgo"] = str(current.get("invitePinnedRootPublicKeyAlgo", "") or "") pinned_root = invite_pinned_root pinned_root_manifest = invite_pinned_root_manifest if not pinned: # First-seen fingerprint — TOFU pin. current["remotePrekeyFingerprint"] = candidate current["remotePrekeyPinnedAt"] = now current["remotePrekeyMismatch"] = False if observed_root: current["remotePrekeyRootFingerprint"] = observed_root current["remotePrekeyRootManifestFingerprint"] = observed_root_manifest current["remotePrekeyRootWitnessPolicyFingerprint"] = observed_root_witness_policy current["remotePrekeyRootWitnessThreshold"] = observed_root_witness_threshold current["remotePrekeyRootWitnessCount"] = observed_root_witness_count current["remotePrekeyRootWitnessDomainCount"] = observed_root_witness_domain_count current["remotePrekeyRootManifestGeneration"] = observed_root_manifest_generation current["remotePrekeyRootRotationProven"] = observed_root_rotation_proven current["remotePrekeyRootPinnedAt"] = now current["remotePrekeyRootMismatch"] = False current["trust_level"] = "tofu_pinned" elif pinned == candidate and (not pinned_root or not observed_root or pinned_root == observed_root): # Same fingerprint — preserve existing trust level (tofu or sas_verified). current["remotePrekeyMismatch"] = bool(transparency_conflict) current["remotePrekeyRootMismatch"] = False if observed_root: current["remotePrekeyRootFingerprint"] = observed_root current["remotePrekeyRootManifestFingerprint"] = observed_root_manifest current["remotePrekeyRootWitnessPolicyFingerprint"] = observed_root_witness_policy current["remotePrekeyRootWitnessThreshold"] = observed_root_witness_threshold current["remotePrekeyRootWitnessCount"] = observed_root_witness_count current["remotePrekeyRootWitnessDomainCount"] = observed_root_witness_domain_count current["remotePrekeyRootManifestGeneration"] = observed_root_manifest_generation current["remotePrekeyRootRotationProven"] = observed_root_rotation_proven if transparency_conflict: trust_changed = True if prior_trust in ("invite_pinned", "sas_verified"): current["trust_level"] = "continuity_broken" else: current["trust_level"] = "mismatch" elif prior_trust in ("mismatch", "continuity_broken"): current["remotePrekeyMismatch"] = True current["remotePrekeyRootMismatch"] = prior_root_mismatch current["remotePrekeyTransparencyConflict"] = prior_transparency_conflict current["trust_level"] = prior_trust elif prior_trust not in ("tofu_pinned", "invite_pinned", "sas_verified"): current["trust_level"] = "invite_pinned" if invite_pinned and pinned == invite_pinned else "tofu_pinned" else: # Changed fingerprint — severity depends on prior verification. trust_changed = True root_changed = bool(observed_root and pinned_root and pinned_root != observed_root) current["remotePrekeyMismatch"] = pinned != candidate current["remotePrekeyRootMismatch"] = root_changed if observed_root and not pinned_root: current["remotePrekeyRootFingerprint"] = observed_root current["remotePrekeyRootManifestFingerprint"] = observed_root_manifest current["remotePrekeyRootWitnessPolicyFingerprint"] = observed_root_witness_policy current["remotePrekeyRootWitnessThreshold"] = observed_root_witness_threshold current["remotePrekeyRootWitnessCount"] = observed_root_witness_count current["remotePrekeyRootWitnessDomainCount"] = observed_root_witness_domain_count current["remotePrekeyRootManifestGeneration"] = observed_root_manifest_generation current["remotePrekeyRootRotationProven"] = observed_root_rotation_proven current["remotePrekeyRootPinnedAt"] = now current["remotePrekeyRootMismatch"] = False root_changed = False if prior_trust in ("invite_pinned", "sas_verified") or bool(invite_pinned_root): current["trust_level"] = "continuity_broken" else: current["trust_level"] = "mismatch" current["updated_at"] = int(time.time()) contacts[peer_key] = _normalize_contact(current) _write_contacts(contacts) return { "ok": True, "peer_id": peer_key, "trust_changed": trust_changed, "trust_level": contacts[peer_key]["trust_level"], "contact": contacts[peer_key], } def confirm_sas_verification( peer_id: str, sas_phrase: str, *, peer_ref: str = "", words: int = 8, ) -> dict[str, Any]: """Record successful SAS verification for a contact. Sets trust_level to sas_verified and updates legacy compat fields. The contact must already be in a verifiable state (tofu_pinned, invite_pinned, or sas_verified for idempotence). Rejects mismatch and continuity_broken to prevent silent re-pin of a changed fingerprint. """ peer_key = str(peer_id or "").strip() if not peer_key: raise ValueError("peer_id required") contacts = _read_contacts() current = _normalize_contact(contacts.get(peer_key)) if not str(current.get("remotePrekeyFingerprint", "") or "").strip(): return {"ok": False, "detail": "no pinned fingerprint to verify"} current_trust = str(current.get("trust_level", "") or "").strip() if current_trust in ("mismatch", "continuity_broken"): return { "ok": False, "trust_level": current_trust, "detail": f"cannot verify: trust_level is {current_trust} — acknowledge the changed fingerprint first", } normalized_phrase = _normalize_sas_phrase(sas_phrase) if not normalized_phrase: return { "ok": False, "trust_level": current_trust or "unpinned", "detail": "sas proof required", } expected = _derive_expected_contact_sas_phrase( peer_key, peer_ref=str(peer_ref or ""), words=max(2, min(int(words or 8), 16)), ) if not bool(expected.get("ok")): return { "ok": False, "trust_level": current_trust or "unpinned", "detail": str(expected.get("detail", "") or "sas phrase unavailable"), } expected_phrase = _normalize_sas_phrase(str(expected.get("phrase", "") or "")) if normalized_phrase != expected_phrase: return { "ok": False, "trust_level": current_trust or "unpinned", "detail": "sas phrase mismatch", } now = int(time.time()) current["trust_level"] = "sas_verified" current["verified"] = True current["verify_inband"] = True current["verified_at"] = now current["remotePrekeyMismatch"] = False current["remotePrekeyRootMismatch"] = False current["verify_mismatch"] = False current["updated_at"] = now contacts[peer_key] = _normalize_contact(current) _write_contacts(contacts) try: from services.mesh.mesh_wormhole_dead_drop import ( AliasRotationReason, maybe_prepare_pairwise_dm_alias_rotation, ) maybe_prepare_pairwise_dm_alias_rotation( peer_id=peer_key, peer_dh_pub=str(current.get("dhPubKey") or current.get("invitePinnedDhPubKey") or ""), reason=AliasRotationReason.CONTACT_VERIFICATION_COMPLETED.value, ) except Exception: pass return { "ok": True, "peer_id": peer_key, "trust_level": "sas_verified", "contact": contacts[peer_key], } def recover_verified_root_continuity( peer_id: str, sas_phrase: str, *, peer_ref: str = "", words: int = 8, ) -> dict[str, Any]: """Explicitly adopt an observed stable-root change after SAS verification. This is only valid for contacts in continuity_broken due to root mismatch. It fetches the current bundle through the existing lookup path, verifies the currently advertised root attestation still matches the observed mismatch, then promotes the contact directly to sas_verified. Old invite-pinned trust anchors are cleared because continuity is now rooted in SAS, not the prior invite chain. """ peer_key = str(peer_id or "").strip() if not peer_key: raise ValueError("peer_id required") contacts = _read_contacts() current = _normalize_contact(contacts.get(peer_key)) current_trust = str(current.get("trust_level", "") or "").strip() if current_trust != "continuity_broken": return { "ok": False, "trust_level": current_trust, "detail": f"root recovery only valid for continuity_broken, current is {current_trust}", } if not bool(current.get("remotePrekeyRootMismatch")): return { "ok": False, "trust_level": current_trust, "detail": "root recovery requires an observed stable root mismatch", } observed = str(current.get("remotePrekeyObservedFingerprint", "") or "").strip().lower() observed_root = str(current.get("remotePrekeyObservedRootFingerprint", "") or "").strip().lower() if not observed or not observed_root: return { "ok": False, "trust_level": current_trust, "detail": "no observed stable-root candidate to recover", } from services.mesh.mesh_wormhole_prekey import fetch_dm_prekey_bundle, verify_bundle_root_attestation lookup_handle = str(current.get("invitePinnedPrekeyLookupHandle", "") or "").strip() fetched = fetch_dm_prekey_bundle(agent_id="" if lookup_handle else peer_key, lookup_token=lookup_handle) if not fetched.get("ok"): return { "ok": False, "trust_level": current_trust, "detail": str(fetched.get("detail", "") or "current prekey bundle unavailable for root recovery"), } current_bundle_root = verify_bundle_root_attestation( { "agent_id": str(fetched.get("agent_id", peer_key) or peer_key), "bundle": dict(fetched.get("bundle") or {}), "public_key": str(fetched.get("public_key", "") or ""), "public_key_algo": str(fetched.get("public_key_algo", "Ed25519") or "Ed25519"), "protocol_version": str(fetched.get("protocol_version", "") or ""), } ) if not current_bundle_root.get("ok"): return { "ok": False, "trust_level": current_trust, "detail": str(current_bundle_root.get("detail", "") or "root attestation invalid"), } fetched_fingerprint = str(fetched.get("trust_fingerprint", "") or "").strip().lower() fetched_root = str(current_bundle_root.get("root_fingerprint", "") or "").strip().lower() if fetched_fingerprint != observed or fetched_root != observed_root: return { "ok": False, "trust_level": current_trust, "detail": "observed root candidate changed again; refresh and compare SAS again before recovery", } normalized_phrase = _normalize_sas_phrase(sas_phrase) if not normalized_phrase: return { "ok": False, "trust_level": current_trust, "detail": "sas proof required", } expected = _derive_expected_contact_sas_phrase( peer_key, peer_ref=str(peer_ref or ""), words=max(2, min(int(words or 8), 16)), peer_dh_pub_override=str(fetched.get("identity_dh_pub_key", "") or ""), ) if not bool(expected.get("ok")): return { "ok": False, "trust_level": current_trust, "detail": str(expected.get("detail", "") or "sas phrase unavailable"), } expected_phrase = _normalize_sas_phrase(str(expected.get("phrase", "") or "")) if normalized_phrase != expected_phrase: return { "ok": False, "trust_level": current_trust, "detail": "sas phrase mismatch", } now = int(time.time()) current["dhPubKey"] = str(fetched.get("identity_dh_pub_key", "") or "") current["dhAlgo"] = str(fetched.get("dh_algo", "X25519") or "X25519") current["remotePrekeyFingerprint"] = observed current["remotePrekeyPinnedAt"] = now current["remotePrekeyLastSeenAt"] = now current["remotePrekeyMismatch"] = False current["remotePrekeyRootFingerprint"] = observed_root current["remotePrekeyObservedRootManifestFingerprint"] = str( current_bundle_root.get("root_manifest_fingerprint", "") or "" ).strip().lower() current["remotePrekeyRootManifestFingerprint"] = str( current_bundle_root.get("root_manifest_fingerprint", "") or "" ).strip().lower() current["remotePrekeyObservedRootWitnessPolicyFingerprint"] = str( current_bundle_root.get("root_witness_policy_fingerprint", "") or "" ).strip().lower() current["remotePrekeyRootWitnessPolicyFingerprint"] = str( current_bundle_root.get("root_witness_policy_fingerprint", "") or "" ).strip().lower() current["remotePrekeyObservedRootWitnessThreshold"] = int( current_bundle_root.get("root_witness_threshold", 0) or 0 ) current["remotePrekeyRootWitnessThreshold"] = int(current_bundle_root.get("root_witness_threshold", 0) or 0) current["remotePrekeyObservedRootWitnessCount"] = int( current_bundle_root.get("root_witness_count", 0) or 0 ) current["remotePrekeyRootWitnessCount"] = int(current_bundle_root.get("root_witness_count", 0) or 0) current["remotePrekeyObservedRootWitnessDomainCount"] = int( current_bundle_root.get("root_witness_domain_count", 0) or 0 ) current["remotePrekeyRootWitnessDomainCount"] = int( current_bundle_root.get("root_witness_domain_count", 0) or 0 ) current["remotePrekeyObservedRootManifestGeneration"] = int( current_bundle_root.get("root_manifest_generation", 0) or 0 ) current["remotePrekeyRootManifestGeneration"] = int(current_bundle_root.get("root_manifest_generation", 0) or 0) current["remotePrekeyObservedRootRotationProven"] = bool( int(current_bundle_root.get("root_manifest_generation", 0) or 0) <= 1 or current_bundle_root.get("root_rotation_proven") ) current["remotePrekeyRootRotationProven"] = bool( int(current_bundle_root.get("root_manifest_generation", 0) or 0) <= 1 or current_bundle_root.get("root_rotation_proven") ) current["remotePrekeyRootPinnedAt"] = now current["remotePrekeyRootLastSeenAt"] = now current["remotePrekeyRootNodeId"] = str(current_bundle_root.get("root_node_id", "") or "") current["remotePrekeyRootPublicKey"] = str(current_bundle_root.get("root_public_key", "") or "") current["remotePrekeyRootPublicKeyAlgo"] = str( current_bundle_root.get("root_public_key_algo", "Ed25519") or "Ed25519" ) current["remotePrekeyRootMismatch"] = False current["invitePinnedTrustFingerprint"] = "" current["invitePinnedNodeId"] = "" current["invitePinnedPublicKey"] = "" current["invitePinnedPublicKeyAlgo"] = "" current["invitePinnedDhPubKey"] = "" current["invitePinnedDhAlgo"] = "" current["invitePinnedRootFingerprint"] = "" current["invitePinnedRootManifestFingerprint"] = "" current["invitePinnedRootWitnessPolicyFingerprint"] = "" current["invitePinnedRootWitnessThreshold"] = 0 current["invitePinnedRootWitnessCount"] = 0 current["invitePinnedRootWitnessDomainCount"] = 0 current["invitePinnedRootManifestGeneration"] = 0 current["invitePinnedRootRotationProven"] = False current["invitePinnedRootNodeId"] = "" current["invitePinnedRootPublicKey"] = "" current["invitePinnedRootPublicKeyAlgo"] = "" current["invitePinnedIssuedAt"] = 0 current["invitePinnedExpiresAt"] = 0 current["invitePinnedAt"] = 0 current["trust_level"] = "sas_verified" current["verified"] = True current["verify_inband"] = True current["verify_registry"] = False current["verify_mismatch"] = False current["verified_at"] = now current["updated_at"] = now contacts[peer_key] = _normalize_contact(current) _write_contacts(contacts) try: from services.mesh.mesh_wormhole_dead_drop import ( AliasRotationReason, maybe_prepare_pairwise_dm_alias_rotation, ) maybe_prepare_pairwise_dm_alias_rotation( peer_id=peer_key, peer_dh_pub=str(current.get("dhPubKey") or current.get("invitePinnedDhPubKey") or ""), reason=AliasRotationReason.CONTACT_VERIFICATION_COMPLETED.value, ) except Exception: pass return { "ok": True, "peer_id": peer_key, "trust_level": "sas_verified", "detail": "stable root continuity recovered via SAS verification", "contact": contacts[peer_key], } def acknowledge_changed_fingerprint(peer_id: str) -> dict[str, Any]: """Explicitly accept a changed observed fingerprint for a contact. Valid only when trust_level is mismatch or continuity_broken and an observed fingerprint exists. Re-pins the current observed fingerprint, clears mismatch flags, clears legacy verified state, and sets trust_level to tofu_pinned. This is NOT sas_verified — the operator must re-confirm SAS separately. """ peer_key = str(peer_id or "").strip() if not peer_key: raise ValueError("peer_id required") contacts = _read_contacts() current = _normalize_contact(contacts.get(peer_key)) current_trust = str(current.get("trust_level", "") or "").strip() if current_trust not in ("mismatch", "continuity_broken"): return { "ok": False, "trust_level": current_trust, "detail": f"acknowledgment only valid for mismatch or continuity_broken, current is {current_trust}", } observed = str(current.get("remotePrekeyObservedFingerprint", "") or "").strip().lower() if not observed: return { "ok": False, "trust_level": current_trust, "detail": "no observed fingerprint to re-pin", } invite_pinned = str(current.get("invitePinnedTrustFingerprint", "") or "").strip().lower() if invite_pinned and observed != invite_pinned: return { "ok": False, "trust_level": current_trust, "detail": "invite-pinned contact requires invite replacement before acknowledging changed fingerprint", } observed_root = str(current.get("remotePrekeyObservedRootFingerprint", "") or "").strip().lower() observed_root_manifest = str(current.get("remotePrekeyObservedRootManifestFingerprint", "") or "").strip().lower() invite_pinned_root = str(current.get("invitePinnedRootFingerprint", "") or "").strip().lower() if bool(current.get("remotePrekeyRootMismatch")): return { "ok": False, "trust_level": current_trust, "detail": "stable root changed; recover root continuity with SAS or replace the signed invite before trusting this contact again", } if invite_pinned_root and observed_root and observed_root != invite_pinned_root: return { "ok": False, "trust_level": current_trust, "detail": "invite-pinned contact requires invite replacement before acknowledging changed stable root", } now = int(time.time()) current["remotePrekeyFingerprint"] = observed current["remotePrekeyPinnedAt"] = now current["remotePrekeyMismatch"] = False if observed_root: current["remotePrekeyRootFingerprint"] = observed_root current["remotePrekeyRootManifestFingerprint"] = observed_root_manifest current["remotePrekeyObservedRootManifestFingerprint"] = observed_root_manifest current["remotePrekeyRootWitnessPolicyFingerprint"] = str( current.get("remotePrekeyObservedRootWitnessPolicyFingerprint", "") or "" ).strip().lower() current["remotePrekeyRootWitnessThreshold"] = int( current.get("remotePrekeyObservedRootWitnessThreshold", 0) or 0 ) current["remotePrekeyRootWitnessCount"] = int( current.get("remotePrekeyObservedRootWitnessCount", 0) or 0 ) current["remotePrekeyRootWitnessDomainCount"] = int( current.get("remotePrekeyObservedRootWitnessDomainCount", 0) or 0 ) current["remotePrekeyRootManifestGeneration"] = int( current.get("remotePrekeyObservedRootManifestGeneration", 0) or 0 ) current["remotePrekeyRootRotationProven"] = bool( int(current.get("remotePrekeyObservedRootManifestGeneration", 0) or 0) <= 1 or current.get("remotePrekeyObservedRootRotationProven") ) current["remotePrekeyRootPinnedAt"] = now current["remotePrekeyRootMismatch"] = False current["trust_level"] = "tofu_pinned" current["verified"] = False current["verify_inband"] = False current["verify_mismatch"] = False current["verified_at"] = 0 current["updated_at"] = now contacts[peer_key] = _normalize_contact(current) _write_contacts(contacts) return { "ok": True, "peer_id": peer_key, "trust_level": "tofu_pinned", "contact": contacts[peer_key], }