84 lines
2.4 KiB
Python
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() |