Files
troupe-standup/wizard-work/spark/file_hash_validator.py
T

84 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""File Hash Validator - verify SHA256 checksums for file integrity.
Usage:
python file_hash_validator.py <file> <expected_sha256>
python file_hash_validator.py --verify <checksum_file>
A checksum file format: "<sha256_hash> <filename>" (same as sha256sum output)
"""
import hashlib
import sys
from pathlib import Path
def compute_sha256(filepath: str) -> str:
"""Compute SHA256 hash of a file."""
sha256_hash = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
sha256_hash.update(chunk)
return sha256_hash.hexdigest()
def verify_file(filepath: str, expected_hash: str) -> bool:
"""Verify a file's hash matches expected value."""
if not Path(filepath).exists():
print(f"ERROR: File not found: {filepath}")
return False
actual = compute_sha256(filepath)
if actual.lower() == expected_hash.lower():
print(f"OK: {filepath}")
print(f" SHA256: {actual}")
return True
else:
print(f"FAIL: {filepath}")
print(f" Expected: {expected_hash}")
print(f" Actual: {actual}")
return False
def verify_checksum_file(checksum_file: str) -> int:
"""Verify all files in a checksum file. Returns count of failed verifications."""
if not Path(checksum_file).exists():
print(f"ERROR: Checksum file not found: {checksum_file}")
return 1
failed = 0
with open(checksum_file, "r") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
parts = line.split()
if len(parts) >= 2:
expected_hash = parts[0]
filepath = parts[1]
if not verify_file(filepath, expected_hash):
failed += 1
else:
print(f"SKIP: Malformed line: {line}")
if failed == 0:
print(f"\nAll files verified successfully.")
else:
print(f"\n{failed} file(s) failed verification.")
return failed
def main():
if len(sys.argv) < 3:
print(__doc__)
sys.exit(1)
if sys.argv[1] == "--verify":
exit_code = verify_checksum_file(sys.argv[2])
else:
exit_code = 0 if verify_file(sys.argv[1], sys.argv[2]) else 1
sys.exit(exit_code)
if __name__ == "__main__":
main()