Files
android-assistant/ci/signing.py
T

95 lines
2.6 KiB
Python

"""GPG signing and SHA-256 hashing utilities for release artifacts."""
import hashlib
import subprocess
import tempfile
from pathlib import Path
def gpg_sign_file(file_path: Path, passphrase: str) -> Path:
"""Create a detached ASCII-armored GPG signature for a file.
Args:
file_path: Path to the file to sign.
passphrase: GPG key passphrase.
Returns:
Path to the generated .asc signature file.
Raises:
subprocess.CalledProcessError: If GPG signing fails.
FileNotFoundError: If the input file does not exist.
"""
if not file_path.exists():
raise FileNotFoundError(f"File not found: {file_path}")
sig_path = file_path.with_suffix(file_path.suffix + ".asc")
with tempfile.NamedTemporaryFile(mode="w", suffix=".pass", delete=True) as passfile:
passfile.write(passphrase)
passfile.flush()
subprocess.run(
[
"gpg",
"--batch",
"--yes",
"--passphrase-file",
passfile.name,
"--detach-sign",
"--armor",
str(file_path),
],
check=True,
capture_output=True,
text=True,
)
print(f"Signed: {sig_path}")
return sig_path
def sha256_hash_file(file_path: Path) -> tuple[str, Path]:
"""Compute SHA-256 hash of a file and write a .sha256 checksum file.
Args:
file_path: Path to the file to hash.
Returns:
Tuple of (hex digest, path to .sha256 file).
Raises:
FileNotFoundError: If the input file does not exist.
"""
if not file_path.exists():
raise FileNotFoundError(f"File not found: {file_path}")
sha256 = hashlib.sha256()
with open(file_path, "rb") as fh:
for chunk in iter(lambda: fh.read(8192), b""):
sha256.update(chunk)
digest = sha256.hexdigest()
hash_line = f"{digest} {file_path.name}"
hash_path = file_path.parent / f"{file_path.stem}.sha256"
hash_path.write_text(hash_line, encoding="ascii")
print(f"SHA-256 ({file_path.name}): {digest}")
return digest, hash_path
def sign_and_hash(file_path: Path, passphrase: str) -> tuple[Path, Path]:
"""Sign a file with GPG and generate its SHA-256 checksum.
Args:
file_path: Path to the artifact to sign and hash.
passphrase: GPG key passphrase.
Returns:
Tuple of (signature path, hash file path).
"""
sig_path = gpg_sign_file(file_path, passphrase)
_, hash_path = sha256_hash_file(file_path)
return sig_path, hash_path