mirror of
https://github.com/saymrwulf/proof-aware-crypto-tooling-agent.git
synced 2026-09-04 20:03:40 +00:00
"Eat your own dogfood": pacta consumes certificates about a verified
Ed25519 implementation while checking those certificates' signatures
with OpenSSL. Now it can use the object of its own evidence:
- dogfood/pacta-verified-verify: a ~90-line Rust binary built against
the PINNED proven source workspace (saymrwulf/curve25519-dalek-source
at the exact commit the dalek certificates pin - the build records it:
aa0f6ab...) with the serial backend pinned via RUSTFLAGS exactly as
the verified extraction pins it. Cargo.toml is committed as a template
({{SOURCE}} placeholder) so no machine path is hardcoded; the rendered
file, target/, and the built binary are gitignored.
- pacta dogfood-build --source <workspace>: renders, builds, installs
to dogfood/state/, and writes a provenance sidecar (source commit,
backend cfg, rustc, and an honest coverage note: the certificates
cover verify_sha512, the extraction-refactored image of this verify
path; SHA-512 and the wire glue remain the theorems' documented
boundary). pacta dogfood-status reports the active backend.
- signing.verify_payload_ed25519_detailed: dispatch - the dogfood
binary when present (backend "verified-dalek-serial"), OpenSSL
fallback otherwise, and the backend that ACTUALLY ran is recorded in
receipt signature statuses and attestation evidence. Fallback is
never silent.
- --require-verified-verifier (receipt-verify + agent): policy fails
closed when verification did not run on the certificate-covered
path.
- ML-DSA is deliberately unchanged: no proven implementation exists,
so the slot stays fail-closed "unavailable" - the honest hybrid-PQC
posture is one proven-classical signature plus one required-but-
unproven PQC slot, never a pretend backend.
Validated live: receipt verification through the proven verifier
(backend recorded), a corrupted signature bit rejected BY the proven
binary, tampered attestations rejected, and the policy failing closed
when the binary is absent. 49/49 tests green (incl. PEM-SPKI raw-key
cross-check against openssl, dispatch/backend recording with a stub,
and a real-binary roundtrip that skips gracefully where unbuilt).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
168 lines
6.9 KiB
Python
168 lines
6.9 KiB
Python
from __future__ import annotations
|
|
|
|
import base64
|
|
import hashlib
|
|
import json
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
class SignatureError(RuntimeError):
|
|
pass
|
|
|
|
|
|
def canonical_attestation_payload(attestation: dict[str, Any]) -> bytes:
|
|
unsigned = {key: value for key, value in attestation.items() if key != "signature"}
|
|
return canonical_json(unsigned)
|
|
|
|
|
|
def canonical_json(document: Any) -> bytes:
|
|
return json.dumps(document, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
|
|
|
|
|
|
def payload_digest(attestation: dict[str, Any]) -> str:
|
|
return hashlib.sha256(canonical_attestation_payload(attestation)).hexdigest()
|
|
|
|
|
|
def public_key_fingerprint(public_key_path: str | Path) -> str:
|
|
data = Path(public_key_path).read_bytes()
|
|
return hashlib.sha256(data).hexdigest()
|
|
|
|
|
|
def generate_ed25519_keypair(private_key_path: str | Path, public_key_path: str | Path) -> None:
|
|
openssl = _openssl()
|
|
private_path = Path(private_key_path)
|
|
public_path = Path(public_key_path)
|
|
private_path.parent.mkdir(parents=True, exist_ok=True)
|
|
public_path.parent.mkdir(parents=True, exist_ok=True)
|
|
subprocess.run([openssl, "genpkey", "-algorithm", "ed25519", "-out", str(private_path)], check=True, timeout=30)
|
|
subprocess.run([openssl, "pkey", "-in", str(private_path), "-pubout", "-out", str(public_path)], check=True, timeout=30)
|
|
|
|
|
|
def sign_attestation(attestation: dict[str, Any], private_key_path: str | Path, public_key_path: str | Path | None = None) -> dict[str, Any]:
|
|
payload = canonical_attestation_payload(attestation)
|
|
signature_base64 = sign_payload_ed25519(payload, private_key_path)
|
|
signed = dict(attestation)
|
|
signed["signature"] = {
|
|
"scheme": "openssl-ed25519",
|
|
"status": "signed",
|
|
"payload_digest_sha256": hashlib.sha256(payload).hexdigest(),
|
|
"signature_base64": signature_base64,
|
|
}
|
|
if public_key_path:
|
|
signed["signature"]["public_key_fingerprint_sha256"] = public_key_fingerprint(public_key_path)
|
|
return signed
|
|
|
|
|
|
def verify_attestation_signature(attestation: dict[str, Any], public_key_path: str | Path) -> tuple[bool, str | None]:
|
|
ok, error, _backend = verify_attestation_signature_detailed(attestation, public_key_path)
|
|
return ok, error
|
|
|
|
|
|
def verify_attestation_signature_detailed(attestation: dict[str, Any], public_key_path: str | Path) -> tuple[bool, str | None, str]:
|
|
signature = attestation.get("signature") or {}
|
|
if signature.get("scheme") != "openssl-ed25519":
|
|
return False, f"Unsupported attestation signature scheme: {signature.get('scheme')}", "none"
|
|
encoded = signature.get("signature_base64")
|
|
if not encoded:
|
|
return False, "Attestation signature is missing signature_base64.", "none"
|
|
expected_digest = signature.get("payload_digest_sha256")
|
|
actual_digest = payload_digest(attestation)
|
|
if expected_digest and expected_digest != actual_digest:
|
|
return False, "Attestation payload digest does not match signature metadata.", "none"
|
|
expected_fingerprint = signature.get("public_key_fingerprint_sha256")
|
|
if expected_fingerprint:
|
|
actual_fingerprint = public_key_fingerprint(public_key_path)
|
|
if expected_fingerprint != actual_fingerprint:
|
|
return False, "Attestation public key fingerprint does not match signature metadata.", "none"
|
|
return verify_payload_ed25519_detailed(canonical_attestation_payload(attestation), encoded, public_key_path)
|
|
|
|
|
|
def sign_payload_ed25519(payload: bytes, private_key_path: str | Path) -> str:
|
|
openssl = _openssl()
|
|
with tempfile.TemporaryDirectory(prefix="pacta-sign-") as tmp:
|
|
payload_path = Path(tmp) / "payload.bin"
|
|
signature_path = Path(tmp) / "payload.sig"
|
|
payload_path.write_bytes(payload)
|
|
completed = subprocess.run(
|
|
[openssl, "pkeyutl", "-sign", "-inkey", str(private_key_path), "-rawin", "-in", str(payload_path), "-out", str(signature_path)],
|
|
check=False,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=30,
|
|
)
|
|
if completed.returncode != 0:
|
|
raise SignatureError((completed.stderr or completed.stdout or "openssl signing failed").strip())
|
|
signature_bytes = signature_path.read_bytes()
|
|
return base64.b64encode(signature_bytes).decode("ascii")
|
|
|
|
|
|
def verify_payload_ed25519(payload: bytes, signature_base64: str, public_key_path: str | Path) -> tuple[bool, str | None]:
|
|
ok, error, _backend = verify_payload_ed25519_detailed(payload, signature_base64, public_key_path)
|
|
return ok, error
|
|
|
|
|
|
def verify_payload_ed25519_detailed(
|
|
payload: bytes,
|
|
signature_base64: str,
|
|
public_key_path: str | Path,
|
|
) -> tuple[bool, str | None, str]:
|
|
"""Verify an Ed25519 signature, preferring pacta's DOGFOOD verifier -
|
|
a binary built from the pinned, certificate-covered dalek source with
|
|
the serial backend pinned - and falling back to OpenSSL when the
|
|
dogfood binary is unavailable. The third element names the backend that
|
|
actually ran so callers can record (and policies can require) the
|
|
verified path."""
|
|
from .dogfood import BACKEND_OPENSSL, BACKEND_VERIFIED, locate_verifier, verify_payload_dogfood
|
|
|
|
try:
|
|
signature_bytes = base64.b64decode(signature_base64, validate=True)
|
|
except ValueError as exc:
|
|
return False, f"Invalid base64 signature: {exc}", "none"
|
|
binary = locate_verifier()
|
|
if binary is not None:
|
|
ok, error = verify_payload_dogfood(payload, signature_bytes, public_key_path, binary)
|
|
return ok, error, BACKEND_VERIFIED
|
|
ok, error = _verify_payload_openssl(payload, signature_bytes, public_key_path)
|
|
return ok, error, BACKEND_OPENSSL
|
|
|
|
|
|
def _verify_payload_openssl(payload: bytes, signature_bytes: bytes, public_key_path: str | Path) -> tuple[bool, str | None]:
|
|
openssl = _openssl()
|
|
with tempfile.TemporaryDirectory(prefix="pacta-verify-") as tmp:
|
|
payload_path = Path(tmp) / "payload.bin"
|
|
signature_path = Path(tmp) / "payload.sig"
|
|
payload_path.write_bytes(payload)
|
|
signature_path.write_bytes(signature_bytes)
|
|
completed = subprocess.run(
|
|
[
|
|
openssl,
|
|
"pkeyutl",
|
|
"-verify",
|
|
"-pubin",
|
|
"-inkey",
|
|
str(public_key_path),
|
|
"-rawin",
|
|
"-in",
|
|
str(payload_path),
|
|
"-sigfile",
|
|
str(signature_path),
|
|
],
|
|
check=False,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=30,
|
|
)
|
|
if completed.returncode != 0:
|
|
return False, (completed.stderr or completed.stdout or "openssl verification failed").strip()
|
|
return True, None
|
|
|
|
|
|
def _openssl() -> str:
|
|
path = shutil.which("openssl")
|
|
if not path:
|
|
raise SignatureError("openssl is not available on PATH")
|
|
return path
|