ACF
acfstandard.io
Developer docs
FR
Signatures

Verify in Python

Verify the Ed25519 signature of an acf-mcp output in Python with the cryptography library (PyPI). Compatible with a Python audit pipeline or an Airflow job.

iNote
One dependency: pip install cryptography. No network call. Verification is constant-time thanks to the libsodium implementation behind cryptography.

Installation

bash
pip install 'cryptography>=42'

Complete snippet

verify_doctrine.pypython
import base64
import json
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
from cryptography.hazmat.primitives.serialization import load_der_public_key
from cryptography.exceptions import InvalidSignature

PUBLIC_KEY_SPKI_B64 = (
    "MCowBQYDK2VwAyEAojtKfh20SGGV63LMETjZBXRWo2tY0viAYziG/y3/L0s="
)

# 1. Decode the SPKI-encoded public key.
spki = base64.b64decode(PUBLIC_KEY_SPKI_B64)
public_key = load_der_public_key(spki)
assert isinstance(public_key, Ed25519PublicKey), (
    "Unexpected key type — acf-mcp uses Ed25519"
)

# 2. Load the signed tool output.
with open("tool-output.json", "r", encoding="utf-8") as f:
    signed = json.load(f)

# 3. The signed message is the doctrine_hash field as UTF-8.
message = signed["doctrine_hash"].encode("utf-8")

# 4. Strip "ed25519:" prefix, decode signature from base64.
sig_b64 = signed["doctrine_signature"].removeprefix("ed25519:")
signature = base64.b64decode(sig_b64)

# 5. Verify.
try:
    public_key.verify(signature, message)
    print("✓ signature valid")
except InvalidSignature:
    print("✗ signature INVALID")

CI integration

To verify every archived output in a CI job: wrap this script as verify(path: str) -> bool and call it on every file under ./audit-trail/*.json. The signature is deterministic, so a regression test pinned on doctrine_hash immediately catches any silent mutation.

Failure modes

  • InvalidSignature the content was tampered with OR the wrong public key is in use.
  • UnsupportedAlgorithm cryptography < 2.6. Upgrade.
  • ValueError sur base64 — signature lost characters on copy-paste (missing = padding).