183 lines
7.3 KiB
Python
183 lines
7.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Validate a privacy-safe legacy bootstrap quiescence receipt."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import asyncio
|
|
import importlib.util
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
INITIALIZER = SCRIPT_DIR / "initialize-public-runtime-upload-root.py"
|
|
|
|
|
|
async def _current_database_target_sha256() -> str:
|
|
import asyncpg
|
|
|
|
from app.config import settings
|
|
from public_runtime_database_identity import connected_database_target_sha256
|
|
|
|
connection = await asyncpg.connect(
|
|
settings.database_url, command_timeout=settings.db_command_timeout
|
|
)
|
|
try:
|
|
async with connection.transaction(isolation="repeatable_read", readonly=True):
|
|
return await connected_database_target_sha256(connection)
|
|
finally:
|
|
await connection.close()
|
|
|
|
|
|
def _load_initializer():
|
|
spec = importlib.util.spec_from_file_location(
|
|
"vignette_upload_initializer_contract", INITIALIZER
|
|
)
|
|
if spec is None or spec.loader is None:
|
|
raise RuntimeError("offline_quiescence_validator_unavailable")
|
|
module = importlib.util.module_from_spec(spec)
|
|
sys.modules[spec.name] = module
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--receipt-path", required=True)
|
|
parser.add_argument("--expected-receipt-sha256", required=True)
|
|
parser.add_argument("--manifest-path", required=True)
|
|
parser.add_argument("--expected-manifest-sha256", required=True)
|
|
parser.add_argument("--stable-source-root", required=True)
|
|
parser.add_argument("--upload-root", required=True)
|
|
parser.add_argument("--expected-legacy-source-commit", required=True)
|
|
parser.add_argument("--expected-legacy-source-tree", required=True)
|
|
args = parser.parse_args()
|
|
module = _load_initializer()
|
|
try:
|
|
manifest_path = module._validated_regular_file(Path(args.manifest_path))
|
|
if module.sha256_file(manifest_path) != args.expected_manifest_sha256:
|
|
raise module.InitializationError("manifest_hash_drift")
|
|
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
|
offline = manifest.get("offline_quiescence") if isinstance(manifest, dict) else None
|
|
if not isinstance(offline, dict) or set(offline) != {
|
|
"receipt_sha256",
|
|
"database_target_sha256",
|
|
"source_root_sha256s",
|
|
"source_root_set_sha256",
|
|
"preserved_object_count",
|
|
"preserved_total_size_bytes",
|
|
"preserved_inventory_sha256",
|
|
"preserved_decode_valid_count",
|
|
"preserved_decode_invalid_count",
|
|
"required_decode_invalid_object_count",
|
|
"required_decode_invalid_reference_count",
|
|
"reference_count",
|
|
"unique_object_count",
|
|
"reference_set_sha256",
|
|
}:
|
|
raise module.InitializationError("manifest_offline_quiescence_missing")
|
|
if offline.get("receipt_sha256") != args.expected_receipt_sha256:
|
|
raise module.InitializationError("manifest_quiescence_receipt_drift")
|
|
proof = module.validate_offline_quiescence_receipt(
|
|
receipt_path=Path(args.receipt_path),
|
|
expected_receipt_sha256=args.expected_receipt_sha256,
|
|
stable_source_root=Path(args.stable_source_root),
|
|
upload_root=Path(args.upload_root),
|
|
expected_source_commit=args.expected_legacy_source_commit,
|
|
expected_source_tree=args.expected_legacy_source_tree,
|
|
expected_source_roots=None,
|
|
expected_source_root_sha256s=offline.get("source_root_sha256s"),
|
|
expected_source_root_set_sha256=offline.get(
|
|
"source_root_set_sha256"
|
|
),
|
|
expected_database_target_sha256=asyncio.run(
|
|
_current_database_target_sha256()
|
|
),
|
|
expected_preserved_object_count=offline.get(
|
|
"preserved_object_count"
|
|
),
|
|
expected_preserved_total_size_bytes=offline.get(
|
|
"preserved_total_size_bytes"
|
|
),
|
|
expected_preserved_inventory_sha256=offline.get(
|
|
"preserved_inventory_sha256"
|
|
),
|
|
expected_preserved_decode_valid_count=offline.get(
|
|
"preserved_decode_valid_count"
|
|
),
|
|
expected_preserved_decode_invalid_count=offline.get(
|
|
"preserved_decode_invalid_count"
|
|
),
|
|
expected_required_decode_invalid_object_count=offline.get(
|
|
"required_decode_invalid_object_count"
|
|
),
|
|
expected_required_decode_invalid_reference_count=offline.get(
|
|
"required_decode_invalid_reference_count"
|
|
),
|
|
expected_reference_count=offline.get("reference_count"),
|
|
expected_unique_object_count=offline.get("unique_object_count"),
|
|
expected_reference_set_sha256=offline.get("reference_set_sha256"),
|
|
)
|
|
if list(proof.source_root_sha256s) != offline.get(
|
|
"source_root_sha256s"
|
|
) or proof.source_root_set_sha256 != offline.get(
|
|
"source_root_set_sha256"
|
|
):
|
|
raise module.InitializationError("manifest_legacy_source_root_drift")
|
|
except (OSError, ValueError, module.InitializationError):
|
|
print(
|
|
json.dumps(
|
|
{"status": "failed", "reason": "offline_quiescence_contract_failed"},
|
|
ensure_ascii=True,
|
|
separators=(",", ":"),
|
|
)
|
|
)
|
|
return 1
|
|
except Exception:
|
|
print(
|
|
json.dumps(
|
|
{"status": "failed", "reason": "offline_quiescence_validation_unavailable"},
|
|
ensure_ascii=True,
|
|
separators=(",", ":"),
|
|
)
|
|
)
|
|
return 1
|
|
print(
|
|
json.dumps(
|
|
{
|
|
"status": "passed",
|
|
"receipt_sha256": proof.receipt_sha256,
|
|
"database_target_sha256": proof.database_target_sha256,
|
|
"source_root_set_sha256": proof.source_root_set_sha256,
|
|
"preserved_object_count": proof.preserved_object_count,
|
|
"preserved_total_size_bytes": proof.preserved_total_size_bytes,
|
|
"preserved_inventory_sha256": proof.preserved_inventory_sha256,
|
|
"preserved_decode_valid_count": (
|
|
proof.preserved_decode_valid_count
|
|
),
|
|
"preserved_decode_invalid_count": (
|
|
proof.preserved_decode_invalid_count
|
|
),
|
|
"required_decode_invalid_object_count": (
|
|
proof.required_decode_invalid_object_count
|
|
),
|
|
"required_decode_invalid_reference_count": (
|
|
proof.required_decode_invalid_reference_count
|
|
),
|
|
"reference_count": proof.reference_count,
|
|
"unique_object_count": proof.unique_object_count,
|
|
"reference_set_sha256": proof.reference_set_sha256,
|
|
"listener_absent": True,
|
|
"tunnel_absent": True,
|
|
},
|
|
ensure_ascii=True,
|
|
separators=(",", ":"),
|
|
)
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|