This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
"""Offline-only passive LF-batch contract for synthetic shsrv inputs.
|
||||
|
||||
The module has no transport, CLI, address, clock, file output or prompt
|
||||
detector. It formats one bounded batch from an already validated Phase-1.0W
|
||||
plan and seals supplied bytes only when the caller supplies a synthetic hard
|
||||
deadline event.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from phase10v_shsrv_collector_model import CollectorError, OfflineCollector
|
||||
from phase10w_shsrv_client_policy import SessionPlan, WINDOW_COMMANDS
|
||||
|
||||
|
||||
IAC = 0xFF
|
||||
MAX_LITERAL_PATH_BYTES = 512
|
||||
MAX_BATCH_BYTES = 1035
|
||||
KNOWN_HELP_FAMILIES = {
|
||||
"OFFICIAL_V07_SOURCE_FAMILY_CANDIDATE",
|
||||
"OFFICIAL_V019_SOURCE_FAMILY_CANDIDATE",
|
||||
}
|
||||
|
||||
|
||||
class PassiveContractError(RuntimeError):
|
||||
"""Fail-closed contract error that never embeds supplied bytes or paths."""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PassiveBatch:
|
||||
"""Target-free immutable outbound bytes for one synthetic session."""
|
||||
|
||||
window: str
|
||||
payload: bytes
|
||||
expected_paths: tuple[str, ...]
|
||||
command_count: int
|
||||
deadline_seconds: int
|
||||
outbound_batches: int = 1
|
||||
lf_only: bool = True
|
||||
telnet_negotiation: bool = False
|
||||
retry_allowed: bool = False
|
||||
reconnect_allowed: bool = False
|
||||
resume_allowed: bool = False
|
||||
|
||||
|
||||
def _validate_payload(payload: bytes) -> None:
|
||||
if not payload or len(payload) > MAX_BATCH_BYTES:
|
||||
raise PassiveContractError("batch length is invalid")
|
||||
if any(value in payload for value in (0x00, 0x0D, IAC)):
|
||||
raise PassiveContractError("batch contains a forbidden byte")
|
||||
if any(value > 0x7F for value in payload):
|
||||
raise PassiveContractError("batch is not ASCII")
|
||||
if not payload.endswith(b"\n"):
|
||||
raise PassiveContractError("batch is not LF terminated")
|
||||
|
||||
|
||||
def _validate_batch(batch: PassiveBatch) -> None:
|
||||
_validate_payload(batch.payload)
|
||||
if batch.outbound_batches != 1 or batch.lf_only is not True or \
|
||||
batch.telnet_negotiation is not False or \
|
||||
batch.retry_allowed is not False or \
|
||||
batch.reconnect_allowed is not False or batch.resume_allowed is not False:
|
||||
raise PassiveContractError("batch safety fields are invalid")
|
||||
if not isinstance(batch.deadline_seconds, int) or isinstance(
|
||||
batch.deadline_seconds, bool) or not 1 <= batch.deadline_seconds <= 10:
|
||||
raise PassiveContractError("batch deadline is invalid")
|
||||
if batch.window == "T2_GREETING_AND_HELP":
|
||||
if batch.payload != b"help\n" or batch.expected_paths != () or \
|
||||
batch.command_count != 1:
|
||||
raise PassiveContractError("help batch contract is invalid")
|
||||
return
|
||||
if batch.window == "T3_ONE_EXACT_PATH" and \
|
||||
len(batch.expected_paths) == 1 and batch.command_count == 2:
|
||||
path = batch.expected_paths[0]
|
||||
try:
|
||||
OfflineCollector._validate_expected_paths({path})
|
||||
encoded = path.encode("ascii", errors="strict")
|
||||
except (CollectorError, UnicodeEncodeError) as error:
|
||||
raise PassiveContractError("path batch contract is invalid") from error
|
||||
expected = b"stat " + encoded + b"\nsum " + encoded + b"\n"
|
||||
if len(encoded) <= MAX_LITERAL_PATH_BYTES and batch.payload == expected:
|
||||
return
|
||||
raise PassiveContractError("path batch contract is invalid")
|
||||
|
||||
|
||||
def build_passive_batch(plan: SessionPlan) -> PassiveBatch:
|
||||
"""Build one target-free LF batch from an exact Phase-1.0W plan."""
|
||||
if not isinstance(plan, SessionPlan):
|
||||
raise PassiveContractError("session plan type is invalid")
|
||||
expected_commands = WINDOW_COMMANDS.get(plan.window)
|
||||
if expected_commands is None or plan.commands != expected_commands:
|
||||
raise PassiveContractError("window command sequence is invalid")
|
||||
if not isinstance(plan.deadline_seconds, int) or isinstance(
|
||||
plan.deadline_seconds, bool) or not 1 <= plan.deadline_seconds <= 10:
|
||||
raise PassiveContractError("deadline is invalid")
|
||||
|
||||
expected_paths: tuple[str, ...]
|
||||
if plan.window == "T2_GREETING_AND_HELP":
|
||||
if plan.exact_literal_path is not None:
|
||||
raise PassiveContractError("help window contains a path")
|
||||
payload = b"help\n"
|
||||
expected_paths = ()
|
||||
elif plan.window == "T3_ONE_EXACT_PATH":
|
||||
path = plan.exact_literal_path
|
||||
if not isinstance(path, str):
|
||||
raise PassiveContractError("exact path is missing")
|
||||
try:
|
||||
OfflineCollector._validate_expected_paths({path})
|
||||
encoded = path.encode("ascii", errors="strict")
|
||||
except (CollectorError, UnicodeEncodeError) as error:
|
||||
raise PassiveContractError("exact path is invalid") from error
|
||||
if len(encoded) > MAX_LITERAL_PATH_BYTES:
|
||||
raise PassiveContractError("exact path is too long")
|
||||
payload = b"stat " + encoded + b"\nsum " + encoded + b"\n"
|
||||
expected_paths = (path,)
|
||||
else:
|
||||
raise PassiveContractError("window is not allowlisted")
|
||||
|
||||
batch = PassiveBatch(
|
||||
window=plan.window,
|
||||
payload=payload,
|
||||
expected_paths=expected_paths,
|
||||
command_count=len(plan.commands),
|
||||
deadline_seconds=plan.deadline_seconds,
|
||||
)
|
||||
_validate_batch(batch)
|
||||
return batch
|
||||
|
||||
|
||||
class PassiveResultAccumulator:
|
||||
"""Consume supplied chunks once; never observes prompts or remote EOF."""
|
||||
|
||||
def __init__(self, batch: PassiveBatch) -> None:
|
||||
if not isinstance(batch, PassiveBatch):
|
||||
raise PassiveContractError("batch type is invalid")
|
||||
_validate_batch(batch)
|
||||
self.batch = batch
|
||||
self._collector = OfflineCollector()
|
||||
self.state = "READY"
|
||||
|
||||
def _invalidate(self, message: str) -> None:
|
||||
if self._collector.state not in {"SEALED", "INVALID", "ABORTED"}:
|
||||
try:
|
||||
self._collector.abort()
|
||||
except CollectorError:
|
||||
pass
|
||||
self.state = "INVALID"
|
||||
raise PassiveContractError(message)
|
||||
|
||||
def feed_supplied_chunk(self, chunk: bytes) -> None:
|
||||
if self.state not in {"READY", "RECEIVING"}:
|
||||
raise PassiveContractError("accumulator is not accepting input")
|
||||
if not isinstance(chunk, bytes):
|
||||
self._invalidate("input must be bytes")
|
||||
if IAC in chunk:
|
||||
self._invalidate("unexpected Telnet control byte")
|
||||
try:
|
||||
self._collector.feed(chunk)
|
||||
except CollectorError as error:
|
||||
self._invalidate("collector rejected supplied input")
|
||||
raise AssertionError("unreachable") from error
|
||||
if chunk:
|
||||
self.state = "RECEIVING"
|
||||
|
||||
def abort(self) -> None:
|
||||
if self.state not in {"READY", "RECEIVING"}:
|
||||
raise PassiveContractError("accumulator can no longer abort")
|
||||
try:
|
||||
self._collector.abort()
|
||||
except CollectorError as error:
|
||||
raise PassiveContractError("collector abort failed") from error
|
||||
self.state = "ABORTED"
|
||||
|
||||
def _validate_complete_result(self, result: dict[str, Any]) -> None:
|
||||
if self.batch.window == "T2_GREETING_AND_HELP":
|
||||
fingerprint = result.get("command_fingerprint", {})
|
||||
if fingerprint.get("source_family_match") not in KNOWN_HELP_FAMILIES:
|
||||
raise PassiveContractError("help response is incomplete or unknown")
|
||||
if result.get("classification") != "SOURCE_FAMILY_FINGERPRINT_ONLY":
|
||||
raise PassiveContractError("help response classification is invalid")
|
||||
elif self.batch.window == "T3_ONE_EXACT_PATH":
|
||||
observations = result.get("file_observations")
|
||||
if not isinstance(observations, list) or len(observations) != 1:
|
||||
raise PassiveContractError("file response is incomplete")
|
||||
observation = observations[0]
|
||||
required = {
|
||||
"path", "metadata_seen", "size", "weak_checksum",
|
||||
"weak_checksum_algorithm", "cryptographic_checksum",
|
||||
"proves_exact_binary",
|
||||
}
|
||||
if not required.issubset(observation) or observation.get("path") != \
|
||||
self.batch.expected_paths[0]:
|
||||
raise PassiveContractError("file response fields are incomplete")
|
||||
if observation.get("metadata_seen") is not True or not isinstance(
|
||||
observation.get("size"), int) or isinstance(
|
||||
observation.get("size"), bool) or observation["size"] < 0:
|
||||
raise PassiveContractError("stat response is incomplete")
|
||||
if observation.get("weak_checksum_algorithm") != "BSD_ROTATE_16" or \
|
||||
observation.get("cryptographic_checksum") is not False:
|
||||
raise PassiveContractError("sum response is incomplete")
|
||||
if result.get("classification") != "WEAK_FILE_CORRELATION_ONLY":
|
||||
raise PassiveContractError("file response classification is invalid")
|
||||
else:
|
||||
raise PassiveContractError("batch window is invalid")
|
||||
|
||||
def seal_at_hard_deadline(
|
||||
self, hard_deadline_reached: bool,
|
||||
) -> dict[str, Any]:
|
||||
"""Seal once only after an externally supplied synthetic deadline."""
|
||||
if hard_deadline_reached is not True:
|
||||
self._invalidate("hard deadline event is absent")
|
||||
if self.state not in {"READY", "RECEIVING"}:
|
||||
raise PassiveContractError("accumulator cannot be sealed")
|
||||
try:
|
||||
result = self._collector.finalize(set(self.batch.expected_paths))
|
||||
self._validate_complete_result(result)
|
||||
except (CollectorError, PassiveContractError) as error:
|
||||
self.state = "INVALID"
|
||||
raise PassiveContractError("deadline result is invalid") from error
|
||||
self.state = "SEALED"
|
||||
result["passive_batch_contract"] = {
|
||||
"offline_only": True,
|
||||
"one_outbound_batch": True,
|
||||
"plain_lf_only": True,
|
||||
"telnet_negotiation_emitted": False,
|
||||
"telnet_control_received": False,
|
||||
"prompt_completion_used": False,
|
||||
"remote_eof_completion_used": False,
|
||||
"sealed_by_synthetic_hard_deadline": True,
|
||||
"source_family_selected": False,
|
||||
"retry_allowed": False,
|
||||
"reconnect_allowed": False,
|
||||
"device_behavior_proven": False,
|
||||
"exact_identity_proven": False,
|
||||
}
|
||||
return result
|
||||
Reference in New Issue
Block a user