Files
archy/Android/scripts/prepare-fips-source.py

90 lines
3.9 KiB
Python

#!/usr/bin/env python3
"""Materialize the pinned stable FIPS source plus the reviewed startup patch.
Uses Cargo's existing git checkout offline when available. A fresh workstation
fetches only the pinned public commit. Never edits Cargo's shared source cache.
Run before standalone Cargo commands; Gradle runs it automatically.
"""
import hashlib
import io
import json
import os
from pathlib import Path
import shutil
import subprocess
import tarfile
import tempfile
PIN = 'f4a5b4a8e61e340e6be7551608109a5c399f8353'
URL = 'https://github.com/jmcorgan/fips'
ANDROID = Path(__file__).resolve().parents[1]
PATCH = ANDROID.parent / 'docs/companion-performance/fips-nonblocking-static-discovery.patch'
DEST = ANDROID / 'rust/fips-stable'
STAMP = '.archy-source.json'
def tree_hash(root):
digest = hashlib.sha256()
for path in sorted(root.rglob('*')):
rel = path.relative_to(root).as_posix()
if rel == STAMP:
continue
if path.is_symlink():
path.resolve().relative_to(root.resolve())
digest.update(rel.encode() + b'\0link\0' + os.readlink(path).encode())
elif path.is_file():
digest.update(rel.encode() + b'\0')
digest.update(hashlib.sha256(path.read_bytes()).digest())
return digest.hexdigest()
def main():
patch_hash = hashlib.sha256(PATCH.read_bytes()).hexdigest()
expected = {'upstream_commit': PIN, 'patch_sha256': patch_hash}
if DEST.exists():
stamp = json.loads((DEST / STAMP).read_text())
if tree_hash(DEST) != stamp['tree_sha256']:
raise RuntimeError('Generated FIPS source was edited; preserve edits before regenerating it')
if all(stamp.get(k) == v for k, v in expected.items()):
print('Verified pinned FIPS source and startup patch')
return
cargo_home = Path(os.environ.get('CARGO_HOME', Path.home() / '.cargo'))
sources = sorted((cargo_home / 'git/checkouts').glob('fips-*/*'))
archive = None
for source in sources:
check = subprocess.run(['git', '-C', str(source), 'cat-file', '-e', PIN + '^{commit}'],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
if check.returncode == 0:
archive = subprocess.check_output(['git', '-C', str(source), 'archive', PIN])
break
DEST.parent.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory(prefix='.prepare-fips-', dir=DEST.parent) as temp:
temp = Path(temp)
if archive is None:
repo = temp / 'upstream.git'
subprocess.run(['git', 'init', '--bare', '-q', str(repo)], check=True)
subprocess.run(['git', '--git-dir=' + str(repo), 'fetch', '--depth=1', URL, PIN], check=True)
actual = subprocess.check_output(['git', '--git-dir=' + str(repo), 'rev-parse', 'FETCH_HEAD'], text=True).strip()
if actual != PIN:
raise RuntimeError('Upstream commit mismatch')
archive = subprocess.check_output(['git', '--git-dir=' + str(repo), 'archive', PIN])
stage = temp / 'source'
stage.mkdir()
with tarfile.open(fileobj=io.BytesIO(archive)) as tar:
tar.extractall(stage, filter='data')
# Isolate git-apply from the enclosing application's repository prefix.
subprocess.run(['git', 'init', '-q', str(stage)], check=True)
subprocess.run(['git', 'apply', '--check', str(PATCH)], cwd=stage, check=True)
subprocess.run(['git', 'apply', str(PATCH)], cwd=stage, check=True)
shutil.rmtree(stage / '.git')
expected['tree_sha256'] = tree_hash(stage)
(stage / STAMP).write_text(json.dumps(expected, indent=2) + '\n')
if DEST.exists():
shutil.rmtree(DEST) # verified generated tree only, never user source
stage.rename(DEST)
print('Prepared stable FIPS ' + PIN + ' with reviewed nonblocking discovery patch')
if __name__ == '__main__':
main()