FTSeffectCodenotary
Chapter 10 · Code Snippet 10.1

The Digital Notary

C2PA reality-handshake validator

A reference implementation of the 'reality handshake' described in Chapter 10. Loads the Content Authenticity Initiative trust anchors, then verifies a file's C2PA manifest for identity and tamper status, returning VERIFIED, WARNING, TAMPERED, or UNVERIFIED.

c2pa_validator.py86 lines · Python
# File: c2pa_validator.py
# Dependency: c2pa-python (official Coalition for Content Provenance
# and Authenticity bindings, contentauth/c2pa-python on GitHub)
# Install: pip install c2pa-python
import hashlib
import json
import urllib.request
import c2pa
# Public trust list maintained by the Content Authenticity Initiative.
# Anchors include Adobe, Microsoft, the BBC, Leica, Sony, Nikon, etc.
TRUST_ANCHORS_URL = "https://contentcredentials.org/trust/anchors.pem"
class RealityValidator:
"""
The Digital Notary: a logic gate for verifying Content Credentials.
Simulates the 'reality handshake' performed by a browser, social
platform, or newsroom verification pipeline.
"""
def __init__(self):
# Load the 'Iron Alliance' trust anchors and bind them to a
# c2pa.Settings object that the Reader will consult during validation.
with urllib.request.urlopen(TRUST_ANCHORS_URL) as response:
anchors_pem = response.read().decode("utf-8")
self.settings = c2pa.Settings.from_dict({
"verify": {"verify_cert_anchors": True},
"trust": {"trust_anchors": anchors_pem},
})
def validate_asset(self, file_path: str) -> dict:
"""
Input: Path to an image or video file.
Output: Verification status (VERIFIED, WARNING, TAMPERED, UNVERIFIED).
"""
# Step 1: Extract the 'Digital Nutrition Label' (Manifest).
try:
with c2pa.Context(self.settings) as context:
with c2pa.Reader(file_path, context=context) as reader:
manifest_json = json.loads(reader.detailed_json())
except Exception:
return {"status": "UNVERIFIED",
"reason": "No credentials found (Gray Zone)"}
active_label = manifest_json.get("active_manifest")
manifests = manifest_json.get("manifests", {})
manifest = manifests.get(active_label, {})
# Step 2: Verify the identity (who signed this?).
# The Reader has already checked the signing certificate against the
# trust anchors loaded above. A trust failure surfaces here as a
# 'validation_status' entry with a code starting with 'signingCredential'.
for status in manifest.get("validation_status", []):
if status.get("code", "").startswith("signingCredential"):
return {"status": "WARNING",
"reason": "Identity unknown or self-signed"}
# Step 3: Verify the integrity (has it been tampered with?).
# The c2pa library validates hard-binding hashes during Reader
# construction. Any mismatch is reported as 'assertion.dataHash.mismatch'.
for status in manifest.get("validation_status", []):
if status.get("code") == "assertion.dataHash.mismatch":
return {"status": "TAMPERED",
"reason": "Hash mismatch (deepfake injection)"}
# Step 4: The handshake is complete.
return {
"status": "VERIFIED",
"issuer": manifest.get("signature_info", {}).get("issuer"),
"claim_generator": manifest.get("claim_generator"),
"ingredients": manifest.get("ingredients", []), # the chain of edits
}
@staticmethod
def _calculate_sha256(file_path: str) -> str:
"""Standard cryptographic hash of the binary file (illustrative;
the c2pa Reader performs hard-binding validation internally)."""
sha = hashlib.sha256()
with open(file_path, "rb") as f:
for block in iter(lambda: f.read(4096), b""):
sha.update(block)
return sha.hexdigest()