60 lines
1.8 KiB
Python
60 lines
1.8 KiB
Python
"""Read-only filesystem and hash verification helpers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import asdict, dataclass
|
|
from hashlib import sha256
|
|
from pathlib import Path
|
|
from typing import Mapping
|
|
|
|
|
|
KNOWN_GAME_HASHES: dict[str, str] = {
|
|
"Game.exe": "3cc0980aac0f65b379ff340ec61750498fb9f620fb6c88aa66281e8ab27023a5",
|
|
"game.dll": "7ea85b19f452962a39c91f967cad22495f066b2d52e8ac37f4e234463d5c4f09",
|
|
"gamese.exe": "c3443e50a93c6c778eff456122c351e5c7d2c1ceaea50e5a650bffe846a05c87",
|
|
"gamese.dll": "0ae6822f5f347f04bd2b0dc947f0286089c711c84703e110f6f8fc6e0d626131",
|
|
}
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class HashVerification:
|
|
path: Path
|
|
expected_sha256: str
|
|
actual_sha256: str | None
|
|
ok: bool
|
|
error: str | None = None
|
|
|
|
def to_dict(self) -> dict[str, str | bool | None]:
|
|
data = asdict(self)
|
|
data["path"] = str(self.path)
|
|
return data
|
|
|
|
|
|
def sha256_file(path: Path, chunk_size: int = 1024 * 1024) -> str:
|
|
if chunk_size <= 0:
|
|
raise ValueError("chunk_size must be positive")
|
|
digest = sha256()
|
|
with path.open("rb") as handle:
|
|
for chunk in iter(lambda: handle.read(chunk_size), b""):
|
|
digest.update(chunk)
|
|
return digest.hexdigest()
|
|
|
|
|
|
def verify_file_hash(path: Path, expected_sha256: str) -> HashVerification:
|
|
expected = expected_sha256.casefold()
|
|
try:
|
|
actual = sha256_file(path)
|
|
except (OSError, ValueError) as exc:
|
|
return HashVerification(path, expected, None, False, str(exc))
|
|
return HashVerification(path, expected, actual, actual == expected)
|
|
|
|
|
|
def verify_hashes(expected: Mapping[Path, str]) -> list[HashVerification]:
|
|
return [verify_file_hash(path, digest) for path, digest in expected.items()]
|
|
|
|
|
|
def verify_known_game_hashes(live_game: Path) -> list[HashVerification]:
|
|
expected = {live_game / name: digest for name, digest in KNOWN_GAME_HASHES.items()}
|
|
return verify_hashes(expected)
|
|
|