- packages/p2p: identity (peer_id from sha256 of RSA pubkey), TLS cert generation with SHA-256 fingerprint, GossipClient (WSS PubSub with per-topic handlers, dedup by msg_id, auto-reconnect), MdnsDiscovery (zeroconf optional dependency, graceful no-op fallback) - packages/relay: new meshnet-relay package — RelayServer (asyncio + websockets) with gossip fanout hub, circuit relay proxy for NAT traversal, peer registry; meshnet-relay CLI - packages/p2p/relay_bootstrap.json: team relay bootstrap list - Tracker: _NodeEntry gains relay_addr, cert_fingerprint, peer_id; both register and heartbeat handlers read and store these optional fields - docs/adr/0010 already written (previous commit) - conftest.py: packages/relay added to sys.path - 18 new tests; 115 passed total, 1 skipped (no regressions) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
65 lines
1.9 KiB
Python
65 lines
1.9 KiB
Python
"""Peer identity — stable peer_id and RSA keypair, persisted to disk."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import stat
|
|
from pathlib import Path
|
|
|
|
_DEFAULT_IDENTITY_PATH = Path.home() / ".config" / "meshnet" / "identity.json"
|
|
|
|
|
|
def _generate_keypair() -> tuple[bytes, bytes]:
|
|
"""Return (private_key_pem, public_key_pem) for a new RSA-2048 keypair."""
|
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
from cryptography.hazmat.primitives import serialization
|
|
|
|
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
|
priv_pem = key.private_bytes(
|
|
serialization.Encoding.PEM,
|
|
serialization.PrivateFormat.PKCS8,
|
|
serialization.NoEncryption(),
|
|
)
|
|
pub_pem = key.public_key().public_bytes(
|
|
serialization.Encoding.PEM,
|
|
serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
)
|
|
return priv_pem, pub_pem
|
|
|
|
|
|
def _peer_id_from_pubkey(pub_pem: bytes) -> str:
|
|
return hashlib.sha256(pub_pem).hexdigest()[:16]
|
|
|
|
|
|
def load_or_create_identity(path: Path | None = None) -> dict:
|
|
"""Return identity dict with peer_id, private_key_pem, public_key_pem.
|
|
|
|
Creates and persists a new identity if none exists at path.
|
|
"""
|
|
p = path or _DEFAULT_IDENTITY_PATH
|
|
if p.exists():
|
|
try:
|
|
data = json.loads(p.read_text())
|
|
if "peer_id" in data and "public_key_pem" in data:
|
|
return data
|
|
except (json.JSONDecodeError, OSError):
|
|
pass
|
|
|
|
priv_pem, pub_pem = _generate_keypair()
|
|
identity = {
|
|
"peer_id": _peer_id_from_pubkey(pub_pem),
|
|
"private_key_pem": priv_pem.decode(),
|
|
"public_key_pem": pub_pem.decode(),
|
|
}
|
|
|
|
p.parent.mkdir(parents=True, exist_ok=True)
|
|
p.write_text(json.dumps(identity, indent=2))
|
|
try:
|
|
os.chmod(p, stat.S_IRUSR | stat.S_IWUSR)
|
|
except OSError:
|
|
pass
|
|
|
|
return identity
|