from __future__ import annotations
import argparse
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import re
import sys
from typing import Any
class ReproducibleReleaseError(RuntimeError):
pass
EXPECTED_IDENTICAL = {
"systemd-resolved",
"resolvectl",
"libnss_resolve.so.2",
"files.sha256",
"rustd-resolved.tar.gz",
}
EXPECTED_ARTIFACTS = EXPECTED_IDENTICAL | {"rust-toolchain.txt"}
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def is_hash(value: Any, length: int) -> bool:
return isinstance(value, str) and re.fullmatch(
rf"[0-9a-f]{{{length}}}", value
) is not None
def arguments() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--directory", required=True, type=Path)
parser.add_argument("--source-commit", required=True)
parser.add_argument("--source-tree", required=True)
parser.add_argument("--upstream-commit", required=True)
parser.add_argument("--expected-daemon-sha256", required=True)
parser.add_argument("--expected-client-sha256", required=True)
parser.add_argument("--expected-nss-sha256", required=True)
return parser.parse_args()
def validate(options: argparse.Namespace) -> None:
directory = options.directory.resolve()
if not directory.is_dir() or directory.is_symlink():
raise ReproducibleReleaseError(
"reproducible release directory is missing or unsafe"
)
for label, value in (
("source commit", options.source_commit),
("source tree", options.source_tree),
("upstream commit", options.upstream_commit),
):
if not is_hash(value, 40):
raise ReproducibleReleaseError(f"{label} is invalid")
for label, value in (
("daemon", options.expected_daemon_sha256),
("client", options.expected_client_sha256),
("NSS module", options.expected_nss_sha256),
):
if not is_hash(value, 64):
raise ReproducibleReleaseError(f"expected {label} hash is invalid")
manifest_path = directory / "manifest.json"
if manifest_path.is_symlink() or not manifest_path.is_file():
raise ReproducibleReleaseError("reproducible manifest is missing or unsafe")
try:
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as error:
raise ReproducibleReleaseError(
f"cannot read reproducible manifest: {error}"
) from error
if not isinstance(manifest, dict):
raise ReproducibleReleaseError("reproducible manifest root is not an object")
if manifest.get("schema") != 2 or manifest.get("reproducible") is not True:
raise ReproducibleReleaseError(
"reproducible manifest schema or result is invalid"
)
if manifest.get("build_count") != 2:
raise ReproducibleReleaseError("reproducible manifest build count differs")
if manifest.get("source_commit") != options.source_commit:
raise ReproducibleReleaseError("reproducible manifest source commit differs")
if manifest.get("source_tree") != options.source_tree:
raise ReproducibleReleaseError("reproducible manifest source tree differs")
if manifest.get("upstream_commit") != options.upstream_commit:
raise ReproducibleReleaseError("reproducible manifest upstream commit differs")
if (
manifest.get("rustc_release") != "1.74.0"
or manifest.get("cargo_release") != "1.74.0"
):
raise ReproducibleReleaseError("reproducible manifest toolchain differs")
for label in ("rustc", "cargo"):
if not is_hash(manifest.get(f"{label}_sha256"), 64):
raise ReproducibleReleaseError(
f"reproducible manifest {label} executable hash is invalid"
)
source_date_epoch = manifest.get("source_date_epoch")
if (
not isinstance(source_date_epoch, int)
or isinstance(source_date_epoch, bool)
or source_date_epoch < 0
or source_date_epoch > 253402300799
):
raise ReproducibleReleaseError(
"reproducible manifest source date epoch is invalid"
)
expected_generated_at = datetime.fromtimestamp(
source_date_epoch, tz=timezone.utc
).isoformat()
if manifest.get("generated_at") != expected_generated_at:
raise ReproducibleReleaseError(
"reproducible manifest generated timestamp differs"
)
identical = manifest.get("byte_identical")
if (
not isinstance(identical, list)
or len(identical) != len(EXPECTED_IDENTICAL)
or set(identical) != EXPECTED_IDENTICAL
):
raise ReproducibleReleaseError(
"reproducible manifest byte-identical set differs"
)
raw_artifacts = manifest.get("artifacts")
if not isinstance(raw_artifacts, list):
raise ReproducibleReleaseError(
"reproducible manifest artifact list is missing"
)
entries: dict[str, dict[str, Any]] = {}
for item in raw_artifacts:
if not isinstance(item, dict):
raise ReproducibleReleaseError(
"reproducible manifest artifact entry is malformed"
)
name = item.get("name")
if (
not isinstance(name, str)
or not name
or Path(name).name != name
or name in entries
):
raise ReproducibleReleaseError(
"reproducible manifest artifact name is invalid"
)
entries[name] = item
if set(entries) != EXPECTED_ARTIFACTS:
raise ReproducibleReleaseError(
"reproducible manifest artifact set differs"
)
paths: dict[str, Path] = {}
for name, entry in entries.items():
path = directory / name
if path.is_symlink() or not path.is_file() or path.resolve().parent != directory:
raise ReproducibleReleaseError(
f"reproducible artifact is missing or unsafe: {name}"
)
size = entry.get("size")
expected = entry.get("sha256")
if (
not isinstance(size, int)
or isinstance(size, bool)
or size < 0
or not is_hash(expected, 64)
or size != path.stat().st_size
or expected != sha256(path)
):
raise ReproducibleReleaseError(
f"reproducible manifest artifact binding differs: {name}"
)
paths[name] = path
expected_hashes = {
"systemd-resolved": options.expected_daemon_sha256,
"resolvectl": options.expected_client_sha256,
"libnss_resolve.so.2": options.expected_nss_sha256,
}
for name, expected in expected_hashes.items():
if entries[name]["sha256"] != expected:
raise ReproducibleReleaseError(f"{name} hash differs")
toolchain = paths["rust-toolchain.txt"].read_text(encoding="utf-8")
if "rustc 1.74.0" not in toolchain or "cargo 1.74.0" not in toolchain:
raise ReproducibleReleaseError("reproducible toolchain evidence differs")
if (
f"rustc_binary_sha256 {manifest['rustc_sha256']}" not in toolchain
or f"cargo_binary_sha256 {manifest['cargo_sha256']}" not in toolchain
):
raise ReproducibleReleaseError(
"reproducible toolchain executable hash evidence differs"
)
def main() -> int:
validate(arguments())
print("canonical reproducible release validation passed")
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except (OSError, ReproducibleReleaseError) as error:
print(f"validate-reproducible-release: {error}", file=sys.stderr)
raise SystemExit(1) from error