"""Tunnel data types — TunnelId, HopConfig, TunnelInfo.""" import struct import time class TunnelId: """4-byte unsigned integer tunnel identifier.""" def __init__(self, tunnel_id: int): if tunnel_id < 0 or tunnel_id >= 2**32: raise ValueError(f"TunnelId must be 0..2^32-1, got {tunnel_id}") self._id = tunnel_id def to_bytes(self) -> bytes: return struct.pack("!I", self._id) @classmethod def from_bytes(cls, data: bytes) -> "TunnelId": if len(data) < 4: raise ValueError(f"TunnelId requires 4 bytes, got {len(data)}") return cls(struct.unpack("!I", data[:4])[0]) @classmethod def from_stream(cls, stream) -> "TunnelId": data = stream.read(4) if len(data) < 4: raise ValueError("TunnelId requires 4 bytes") return cls.from_bytes(data) def is_zero(self) -> bool: return self._id == 0 def __int__(self) -> int: return self._id def __eq__(self, other) -> bool: if not isinstance(other, TunnelId): return NotImplemented return self._id == other._id def __hash__(self) -> int: return hash(self._id) def __repr__(self) -> str: return f"TunnelId({self._id})" class HopConfig: """Configuration for one hop in a tunnel.""" def __init__(self, receive_tunnel_id: TunnelId, send_tunnel_id: TunnelId, receive_key: bytes, send_key: bytes, iv_key: bytes, reply_key: bytes, reply_iv: bytes, layer_key: bytes): self.receive_tunnel_id = receive_tunnel_id self.send_tunnel_id = send_tunnel_id self.receive_key = receive_key self.send_key = send_key self.iv_key = iv_key self.reply_key = reply_key self.reply_iv = reply_iv self.layer_key = layer_key class TunnelInfo: """Metadata about a built tunnel.""" def __init__(self, tunnel_id: TunnelId, gateway: bytes, length: int, creation_time: int, expiration: int): if len(gateway) != 32: raise ValueError(f"Gateway hash must be 32 bytes, got {len(gateway)}") self.tunnel_id = tunnel_id self.gateway = gateway self.length = length self.creation_time = creation_time self.expiration = expiration def is_expired(self, now_ms: int | None = None) -> bool: if now_ms is None: now_ms = int(time.time() * 1000) return now_ms >= self.expiration def __eq__(self, other) -> bool: if not isinstance(other, TunnelInfo): return NotImplemented return (self.tunnel_id == other.tunnel_id and self.gateway == other.gateway and self.length == other.length and self.creation_time == other.creation_time and self.expiration == other.expiration) def __repr__(self) -> str: return f"TunnelInfo(id={self.tunnel_id}, length={self.length})"