#!/usr/bin/env python3 # SPDX-License-Identifier: GPL-3.0-or-later """Sanitize and classify an already-supplied shsrv transcript offline. This tool has no networking and never preserves serial or telemetry values. It cannot establish an exact deployed binary identity. """ from __future__ import annotations import argparse import hashlib import json import re import sys from typing import Any CURRENT_COMMAND_HASH = ( "f41168292e205590bda1d243cdf727044e0af280a89fb0c070f4c5d6c92f2fd7") V07_COMMAND_HASH = ( "40313637116b532f3c7f9bebe2c23c0018fe7d4093840cf463a22ba0314ca021") def command_hash(commands: list[str]) -> str: normalized = "\n".join(sorted(set(commands))) return hashlib.sha256(normalized.encode("utf-8")).hexdigest() def parse_transcript( transcript: str, expected_paths: set[str] | None = None, ) -> dict[str, Any]: """Return a sanitized, deliberately non-exact identity record.""" allowed_paths = expected_paths or set() lines = transcript.splitlines() compile_date = None compile_time = None firmware = None commands: list[str] = [] observations: dict[str, dict[str, Any]] = {} serial_discarded = False telemetry_discarded = False greeting_seen = False in_help = False current_observation_path = None greeting_pattern = re.compile( r"Welcome to shsrv\.elf running on pid \d+, " r"compiled (.+?) at ([0-9:]+)") command_pattern = re.compile(r"^\s{2}([A-Za-z0-9_]+)(?:\s+-.*)?$") weak_sum_pattern = re.compile(r"^([0-9]{5})\s+(.+)$") for line in lines: greeting = greeting_pattern.search(line) if greeting: greeting_seen = True compile_date = greeting.group(1).strip() compile_time = greeting.group(2).strip() continue stripped = line.strip() if stripped.startswith("S/N:"): serial_discarded = True continue if stripped.startswith(("SoC temp:", "CPU temp:", "CPU freq:")): telemetry_discarded = True continue if stripped.startswith("Model:"): continue if stripped.startswith("S/W:"): firmware = stripped.split(":", 1)[1].strip() continue if stripped == "Builtin commands:": in_help = True continue if in_help: command = command_pattern.match(line) if command: commands.append(command.group(1)) continue if stripped == "": in_help = False if stripped.startswith("filename:"): path = stripped.split(":", 1)[1].strip() if path in allowed_paths: observations.setdefault(path, {})["metadata_seen"] = True current_observation_path = path else: current_observation_path = None continue if ":" in stripped and current_observation_path is not None: key, value = (part.strip() for part in stripped.split(":", 1)) if key in {"size", "mtime", "ctime"} and value.isdigit(): observations[current_observation_path][key] = int(value) continue weak_sum = weak_sum_pattern.match(stripped) if weak_sum and weak_sum.group(2) in allowed_paths: path = weak_sum.group(2) observation = observations.setdefault(path, {}) observation["weak_checksum"] = weak_sum.group(1) observation["weak_checksum_algorithm"] = "BSD_ROTATE_16" observation["cryptographic_checksum"] = False normalized_commands = sorted(set(commands)) fingerprint = command_hash(normalized_commands) if normalized_commands else None family = "UNRESOLVED" if fingerprint == CURRENT_COMMAND_HASH: family = "OFFICIAL_V019_SOURCE_FAMILY_CANDIDATE" elif fingerprint == V07_COMMAND_HASH: family = "OFFICIAL_V07_SOURCE_FAMILY_CANDIDATE" classification = "INVALID_OR_INCOMPLETE" if greeting_seen: classification = "COMPILE_METADATA_ONLY" if greeting_seen and normalized_commands: classification = "SOURCE_FAMILY_FINGERPRINT_ONLY" if greeting_seen and observations: classification = "WEAK_FILE_CORRELATION_ONLY" return { "schema_version": 1, "classification": classification, "exact_identity": False, "compile_metadata": { "date": compile_date, "time": compile_time, "firmware": firmware, }, "sensitive_input": { "serial_line_seen": serial_discarded, "serial_value_retained": False, "telemetry_line_seen": telemetry_discarded, "telemetry_values_retained": False, }, "command_fingerprint": { "count": len(normalized_commands), "sha256": fingerprint, "source_family_match": family, "commands": normalized_commands, "proves_exact_binary": False, }, "file_observations": [ {"path": path, **value, "proves_exact_binary": False} for path, value in sorted(observations.items()) ], } def main() -> int: parser = argparse.ArgumentParser() parser.add_argument( "--expected-path", action="append", default=[], help="Literal pre-approved path whose metadata may be retained") args = parser.parse_args() result = parse_transcript(sys.stdin.read(), set(args.expected_path)) print(json.dumps(result, indent=2, sort_keys=True)) return 0 if __name__ == "__main__": raise SystemExit(main())