#!/usr/bin/env python3 """Verify and restore the complete reproducibility package; run no proof code. From the extracted submission directory: python3 anc/unpack_reproducibility.py python3 longsmall_reproducibility/anc/check_package.py Only Python's standard library is required to unpack. Full computations require the software documented in the restored anc/README.md and case READMEs. """ import argparse import base64 import binascii import hashlib import json import os from pathlib import Path, PurePosixPath import re import sys ROOT = Path(__file__).resolve().parent.parent FORMAT = "longsmall-complete-consolidated-v1" def checked_path(value): if not isinstance(value, str) or not value: raise ValueError("Missing or invalid file path") path = PurePosixPath(value) if (path.is_absolute() or any(p in ("", ".", "..") for p in value.split("/")) or "\\" in value or ":" in value or "\x00" in value): raise ValueError("Unsafe file path: " + repr(value)) return path def verify_bytes(data, record, label): digest = record.get("sha256") if not isinstance(digest, str) or not re.fullmatch(r"[0-9a-f]{64}", digest): raise ValueError("Invalid checksum for " + label) if len(data) != record["size_bytes"]: raise ValueError("Size mismatch: " + label) if hashlib.sha256(data).hexdigest() != digest: raise ValueError("SHA-256 mismatch: " + label) def read_checked(root, record): relative = checked_path(record["path"]) path = root.joinpath(*relative.parts) data = path.read_bytes() verify_bytes(data, record, record["path"]) return data def load_package(): manifest = json.loads((ROOT / "anc/manifest.json").read_text(encoding="utf-8")) if manifest.get("format") != FORMAT: raise ValueError("Unsupported manifest format") uploaded = {} for record in manifest["uploaded_files"]: name = record["path"] if name in uploaded: raise ValueError("Duplicate uploaded path: " + name) uploaded[name] = read_checked(ROOT, record) required = { "longsmall.tex", "references.bib", "anc/README.json", "anc/sources.json", "anc/data.json", "anc/documentation.json", "anc/unpack_reproducibility.py", } if set(uploaded) != required: raise ValueError("Unexpected submission file inventory") restored = {} def add(record, data): name = record["path"] checked_path(name) if name in restored: raise ValueError("Duplicate restored path: " + name) verify_bytes(data, record, name) mode = record["mode"] if type(mode) is not int or mode < 0 or mode > 0o777: raise ValueError("Invalid permissions: " + name) restored[name] = (data, mode) roots = manifest["root_files"] if {r["path"] for r in roots} != {"longsmall.tex", "references.bib"}: raise ValueError("Unexpected root files") for record in roots: add(record, uploaded[record["path"]]) for name in ("sources", "data", "documentation"): bundle = json.loads(uploaded["anc/" + name + ".json"].decode("utf-8")) if bundle.get("format") != FORMAT or bundle.get("category") != name: raise ValueError("Unexpected bundle format: " + name) for record in bundle["files"]: if not record["path"].startswith("anc/"): raise ValueError("Bundled path must start with anc/") if record["encoding"] == "utf-8": data = record["text"].encode("utf-8") elif record["encoding"] == "base64": data = base64.b64decode(record["base64"], validate=True) else: raise ValueError("Unsupported file encoding: " + record["path"]) add(record, data) if len(restored) != manifest["restored_file_count"]: raise ValueError("Restored file count does not match manifest") for name in restored: for parent in PurePosixPath(name).parents: if str(parent) in restored: raise ValueError("A file conflicts with a directory: " + str(parent)) # Preserve the original checksum file and verify its complete inventory. sums = restored["anc/SHA256SUMS"][0].decode("ascii").splitlines() covered = set() for line in sums: digest, name = line.split(" ", 1) checked_path(name) if name in covered or name not in restored: raise ValueError("Invalid original checksum inventory: " + name) covered.add(name) if hashlib.sha256(restored[name][0]).hexdigest() != digest: raise ValueError("Original checksum mismatch: " + name) if covered != set(restored) - {"anc/SHA256SUMS"}: raise ValueError("Original checksum inventory is incomplete") return restored def unpack(files, output): # Never overwrite an existing directory, including an empty one or a symlink. output = Path(os.path.abspath(os.path.expanduser(str(output)))) if output.exists() or output.is_symlink(): raise ValueError("Output already exists; choose a new directory: " + str(output)) output.mkdir(parents=True) for name, (data, mode) in sorted(files.items()): path = output.joinpath(*checked_path(name).parts) path.parent.mkdir(parents=True, exist_ok=True) with path.open("xb") as handle: handle.write(data) if os.name != "nt": path.chmod(mode) if path.read_bytes() != data: raise OSError("Read-back verification failed: " + str(path)) return output def main(): parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("--output", type=Path, default=ROOT / "longsmall_reproducibility", help="new output directory (default: longsmall_reproducibility next to the source)") parser.add_argument("--verify-only", action="store_true", help="validate every bundled file and original checksum without writing files") parser.add_argument("--list", action="store_true", dest="list_files", help="print the complete original file inventory after verification") args = parser.parse_args() try: files = load_package() print("PASS: all 719 original files and all 718 original checksums verified.") if args.list_files: print("\n".join(sorted(files))) if args.verify_only or args.list_files: return 0 output = unpack(files, args.output) print("Restored all 719 files to: " + str(output)) print("Build instructions and requirements: " + str(output / "anc/README.md")) print("No proof certificates or other restored programs were executed.") return 0 except (OSError, ValueError, KeyError, TypeError, binascii.Error) as exc: print("ERROR: " + str(exc), file=sys.stderr) return 1 if __name__ == "__main__": sys.exit(main())